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


假设我有这段代码:

@interface Foo : NSObject {
    Bar *bar;
}

@property (retain, nonatomic) Bar *bar;

@end

使用此字段/属性时,行之间是否有任何区别:

[self.bar doStuff];

[bar doStuff];

在进行赋值时,属性方法将执行正确的保留,但如上所述,对属性的读访问权如何?有什么区别吗?


3501
2017-07-04 14:16


起源



答案:


有一个很大的不同。 [self.bar doStuff] 相当于 [[self bar] doStuff]

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

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

如果你使用 @synthesize 对你的指示 bar 属性,编译器将为您生成两种方法:

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

还要注意,编译器生成的setter方法是保留你的 Bar 正如你在中所说的那样 @property 宣言。


13
2017-07-04 14:53



然而,尽管吸气剂和场选择器机制之间存在差异,但所提到的线之间仍然没有功能差异。即同一个对象会收到相同的消息,对吧? - Sergey Mikhanov
好吧,可以。因为您可以以任何方式自由地实现方法栏。例如,您可以返回代理对象或其他任何内容。即使在您合成访问器方法的情况下,它们的行为也可能不同,例如,如果将属性设置为原子,则同步访问。 - nschmidt
但是在我合成属性的情况下,即使存在同步,保留或其他任何东西,所提到的行之间仍然没有功能差异,在某种意义上说 同一个对象 收到 同样的消息, 对? - Sergey Mikhanov
肯定有区别。派生类可以覆盖 - (Bar *)bar并返回另一个对象。 - Nikolai Ruhe


答案:


有一个很大的不同。 [self.bar doStuff] 相当于 [[self bar] doStuff]

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

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

如果你使用 @synthesize 对你的指示 bar 属性,编译器将为您生成两种方法:

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

还要注意,编译器生成的setter方法是保留你的 Bar 正如你在中所说的那样 @property 宣言。


13
2017-07-04 14:53



然而,尽管吸气剂和场选择器机制之间存在差异,但所提到的线之间仍然没有功能差异。即同一个对象会收到相同的消息,对吧? - Sergey Mikhanov
好吧,可以。因为您可以以任何方式自由地实现方法栏。例如,您可以返回代理对象或其他任何内容。即使在您合成访问器方法的情况下,它们的行为也可能不同,例如,如果将属性设置为原子,则同步访问。 - nschmidt
但是在我合成属性的情况下,即使存在同步,保留或其他任何东西,所提到的行之间仍然没有功能差异,在某种意义上说 同一个对象 收到 同样的消息, 对? - Sergey Mikhanov
肯定有区别。派生类可以覆盖 - (Bar *)bar并返回另一个对象。 - Nikolai Ruhe


使用访问者self.bar转换为方法调用:[self bar]。句点语法仅适用于外观。直接访问成员变量不涉及额外的函数调用,因此稍快一些。它真的唯一的问题是你是在循环中访问它,还是在某些过程中,这种差异会加起来。 (在iPhone上)为属性创建的setter也有一些额外的开销来进行键值编码。当您调用“setBar:”或说“self.bar =”时会发送KVO通知,因此反复调用它会导致大量通知。

但Jim是对的 - 非原子@property与代码中的变量直接使用之间没有功能差异。除非你真的关心速度,否则使用房产可能是你最好的选择。


2
2017-07-04 14:51



不。考虑一个覆盖的派生类 - (Bar *)bar; - Nikolai Ruhe
好点尼古拉 - 由于被覆盖的 - (Bar *)栏可能会添加额外的业务逻辑或返回除bar之外的其他东西,使用该属性可能不等同于对变量的直接引用。感谢您指出了这一点! - Ben Gotow
需要明确的是:你所谈论的开销并不依赖于通过@synthesize语句创建的setter。即使您实施它们,如果观察者已注册,也会发送通知。这是通过调配来完成的。 - nschmidt


合成的(或正确的手写)非原子访问器在功能上等同于

- (Bar *)bar
{
    return bar;
}

所以你的两个例子之间没有功能差异。

但是,在-dealloc或您的初始化程序之外,始终通过其访问者访问该属性是个好主意。


0
2017-07-04 14:41





如果使用Bar类的方便构造函数为字段赋值,则Bar字段将比具有Retain选项的Bar Property更快成为Zombie,因为引用计数不会通过分配字段而增加,有时您会遇到“访问”解除分配的对象“错误。


0
2018-05-27 08:18