KVC和Properties有什么区别?

8
所以,我已经阅读了记录在文档中的信息。
Objective-C 2.0的点语法和键值编码是正交技术。您可以使用键值编码而不使用点语法,也可以使用点语法而不使用KVC。两者都使用“点语法”。在键值编码的情况下,该语法用于分隔关键路径中的元素。重要的是要记住,当您使用点语法访问属性时,会调用接收器的标准访问器方法。
然后它提供了一个例子,据说展示了两者之间的区别。但是,我仍然不明白KVC和属性访问器方法之间有什么区别?它们不是一样的吗?我如何区分调用setValue:forKeyPath:和简单访问器的点?
2个回答

15

然而,我仍然不明白KVC和属性访问器方法之间有什么区别?

KVC是一种调用属性访问器方法或以其他方式访问属性的方式。

什么是“以其他方式访问”?对于KVC而言,没有访问器方法的实例变量被视为非正式属性。如果找不到相匹配的访问器对,则会直接获取或设置实例变量的值。(是的,在现代代码中使用这种方式没有意义。对于您打算在其他地方访问的任何内容,请始终声明一个@property,并且反过来,不要使用KVC来访问任何不是公共属性的内容。)

属性访问器方法是KVC将调用的方法(优先使用,无论是由KVC还是每个明智的程序员都是如此),而不是直接访问ivar。访问器可以获取或设置实例变量,就像合成访问器一样,也可以访问其他存储方式。

访问器是实现,属性是接口,KVC是使用它们的一种方式。

那么我如何区分调用setValue:forKeyPath:和简单访问器之间的点(.)?

键路径是一个字符串,而属性访问表达式是一个表达式。编译器会评估属性访问表达式并将其转换为一个或多个Objective-C消息,而键路径则由KVC在运行时评估。

因此,当您使用键路径:

[someObject setValue:theValue forKeyPath:@"foo.bar.baz"];
你知道它是一个关键路径,因为(1)它是一个字符串,如在此示例中所指示的字符串文字语法@"...",并且(2)你将关键路径字符串传递给setValue:forKeyPath:以进行评估。
使用关键路径使用KVC访问命名属性。它会代表您发送任何相关的访问器消息。
当你使用属性访问表达式时:
someObject.foo.bar.baz = theValue;

由于您没有使用字符串来标识属性,因此它是一个访问属性表达式。您在自己的代码中访问它们(发送访问器消息)。

在任何形式下,使用KVC的理由并不多;当您在编写/编译时就知道属性时,最好声明一个@property并自己访问该属性,无论是使用属性访问表达式还是消息表达式([[[someObject foo] bar] setBaz:theValue])。使用KVC的时间是在运行时才知道要访问哪个属性的情况下,这种情况非常罕见。它主要是KVO、Cocoa绑定、核心动画等技术背后的构建块。

通常,您只需要自己访问属性。


4
当你在运行时才知道要访问哪个属性时,就可以使用KVC。 - km3h
这对我非常有帮助。我一直认为“在运行时不知道要访问哪个属性时使用KVC”,这可能是原因,但我不确定。你让我对自己的想法更有信心了。 - Hemant Sharma
Peter你说过:“反过来,不要使用KVC访问任何不是公共属性的东西。”我刚刚发现,如果一个属性被声明为只读,然后在实现中重新声明为可读写,则KVC会毫不羞耻地写入该属性。难道没有办法阻止这种情况吗?我尝试了(BOOL)accessInstanceVariablesDirectly{return NO;}但这并不能阻止KVC写入该属性。 - W S
@WS:不是的。KVC早于正式属性,没有“readonly”和“readwrite”的概念,更不用说公开声明为X和私有声明为Y了。它只关心是否有getter或getter和setter。如果属性是“readwrite”,即使只是私有的,那么setter也存在——Objective-C中的方法没有公共/私有区别,因此如果存在,则对所有方法都存在。 - Peter Hosey

2
关键值编码允许您通过使用属性的字符串名称来设置和获取属性的值。例如,如果我有一个名为foo的属性,它的类型为NSString:
[self setValue:@"mystring" forKey:@"foo"];

// read the value by key
NSString *s =  [self valueForKey:@"foo"];

点语法是编译语法糖。作为个人偏好(有些人不同意-没关系),我不使用点语法,但我仍然使用KVC:
[myObj setFoo: @"someString"]

等于:
myObj.foo = @"someString";

它们是正交的,不同的概念,但都处理你如何与属性进行交互。
最后,您提到了属性语法。这是另一个正交的概念,但与处理属性相关。
在Objective-C中,惯例很重要。遵循它们。属性是获取和设置属性名称,赋值为[Name]的名称:
- (NSString*)foo
{
    return _foo;  // defined as (NSString*)_foo in header
}

- (void) setFoo: (NSString*)foo
{
    if (foo == _foo)
        return;

    NSString* curr = _foo;

    _foo = [foo retain];
    [curr release];    
}

“现在,谁想每次都写那样的东西呢。因此,使用@property语法:”
“在头文件中:”
@property (retain) NSString *foo;

然后在 .m 文件中:
@synthesize foo;

这相当于手写的属性访问器。它是编译器语法糖,根据你如何标记属性来扩展属性代码。
文档:

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueCoding/Articles/KeyValueCoding.html

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接