访问属性方法和类字段的区别(Objective-C)

11

假设我有这段代码:

@interface Foo : NSObject {
    Bar *bar;
}

@property (retain, nonatomic) Bar *bar;

@end

使用此字段或属性时,以下两种方式有什么区别:

[self.bar doStuff];

而且

[bar doStuff];

在进行赋值操作时,属性方法会执行正确的保留(retaining)操作,但是在读取属性时是否也存在上述区别呢?

4个回答

13

有很大的区别。

[self.bar doStuff] 相当于 [[self bar] doStuff]

[bar doStuff] 相当于 [self->bar doStuff]

前者使用访问器方法,后者直接访问实例变量 bar。

如果在你的 bar 属性上使用 @synthesize 指令,编译器将为您生成两个方法:

- (void)setBar:(Bar*)b;
- (Bar*)bar;

请注意,编译器生成的setter方法会按照你在@property声明中告诉它的方式保留你的Bar实例。


1
然而,尽管getter和字段选择器机制之间存在差异,但是所提到的行之间仍然没有功能上的区别。也就是说,相同的对象将接收相同的消息,对吗? - Sergey Mikhanov
3
可以这样说,因为你可以自由地以任何方式实现方法bar。例如,你可以返回代理对象或其他内容。即使你合成访问器方法,它们的行为也可能不同,例如如果将属性设置为原子性,则可以同步访问。 - nschmidt
但是,如果我综合属性,即使存在同步、保留或其他任何操作,也不存在上述行之间的功能差异,因为“相同对象”接收“相同消息”,对吧? - Sergey Mikhanov
1
肯定有区别。派生类可以重写-(Bar*)bar并返回另一个对象。 - Nikolai Ruhe

2
使用访问器self.bar被转换为方法调用:[self bar]。句号语法只是为了好看。直接访问成员变量不涉及额外的函数调用,因此速度略快。这只在你在循环中访问它或者在一些需要累积差异的进程中才会起到作用。(在iPhone上)为属性创建的设置器还有一些额外的开销来执行键值编码。当你调用“setBar:”或者说“self.bar =”时,会发送一个KVO通知,因此如果反复调用它将导致大量通知的洪水。
Jim是对的,虽然非原子属性和在你的代码中直接使用变量之间没有功能上的区别。除非你真的关心速度,否则使用属性可能是你最好的选择。

1
不考虑一个派生类,它重写了 -(Bar*)bar; 方法。 - Nikolai Ruhe
好观点,Nikolai - 由于重写的-(Bar*)bar可能会添加额外的业务逻辑或返回其他内容,而使用属性可能不等同于对变量的直接引用。感谢指出这一点! - Ben Gotow
只是为了明确:你所谈论的开销并不取决于通过 @synthesize 语句创建 setter。即使你自己实现它们,如果有观察者注册,通知也会被发送。这是通过 isa swizzling 实现的。 - nschmidt

0
一个合成的(或者正确手写的)非原子访问器在功能上等同于
- (Bar *)bar
{
    return bar;
}

所以在你的两个示例之间没有功能上的区别。

然而,在-dealloc或者初始化器之外,通过其访问器来一致地访问属性是一个好主意。


0
如果您使用Bar类的方便构造函数为字段分配值,那么相比使用保留选项的Bar属性,您的Bar字段将更快地成为Zombie,因为通过为字段赋值不会增加引用计数,有时会遇到“访问已释放对象”的错误。

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