在 Objective-C 中,这一点语法让我有点困惑。
我什么时候应该调用 self.myObject 而不是只调用 myObject?
这似乎是多余的,但它们不可互换。
有人请赐教吗?
在 Objective-C 中,这一点语法让我有点困惑。
我什么时候应该调用 self.myObject 而不是只调用 myObject?
这似乎是多余的,但它们不可互换。
有人请赐教吗?
如果您只是访问它们,则没有太多理由使用self.member
. 如果你在做赋值,那么如果你做的不仅仅是简单的@property (assign)
参数——例如,保留、复制等,那很好,这样它就可以节省你正在编写的代码。一个例子:
myObject = anotherObject;
self.myObject = anotherObject;
第二个选择将确保您实际上以您想要的方式分配对象(获取副本,增加保留计数等)。它和 没有什么不同[self setMyObject:anotherObject]
。
由于点符号被编译器替换为消息(类似于在常规数组中x[5]
的*(x + 5*sizeof(x))
工作方式),因此在常规消息上使用点符号没有开销或额外的效率。
嗯,我不能说我同意 Mark 或 Cinder6。
好吧,我同意第一部分。:-)self.foo
正在调用该-foo
方法。Plainfoo
正在访问foo
ivar。
在大多数情况下,您应该始终通过您的方法。它们可以将您从实际存储中抽象出来,并远离任何其他可能需要的行为。想想如果你以后对你的类进行子类化会发生什么。在大多数情况下,您希望在访问它们所涵盖的功能的地方调用自己的公共方法。
例外是在对象初始化和拆卸中,以及在您不合成它们时在属性访问器本身中。在对象初始化和拆卸期间,您不希望调用方法的子类实现,因为这些方法不必处理处于部分设置状态的对象。
您几乎不应该从同一个类的实现中调用属性访问器。类的实例方法可以方便地访问内部状态,因此直接访问该状态通常是有意义的。
如果您使用的是合成访问器,那么调用它们只会增加(可能)不必要的开销。如果访问器有更复杂的实现,那么你只是模糊了代码的目的。
最后,一些刚接触 Objective-C 的人使用 self.property 语法和综合访问器来避免必须了解 Cocoa 内存管理。你确实需要了解它是如何工作的,所以试图避免这样做会适得其反。
如果您使用的是 Core Data,则基本上应该始终使用访问器,因为在需要之前可能不会从持久存储中加载某些属性。(无论如何,假设您使用的是 SQLite 商店。)
如果您不使用 Core Data,那么myObject
如果您只读取值,则通常直接使用是安全的。如果要修改 的值myObject
,则需要使用访问器以确保正确通知观察该属性值的任何其他对象。换句话说:
// Not changing the value of myObject, so no accessor needed
[someMutableArray addObject:myObject];
// Changes the value, so you need to use the accessor
self.myObject = newObject;
[self setMyObject:newObject]; // Exactly identical to the previous line.
不过,总的来说,开销很小。我更喜欢始终使用访问器,即使在同一个对象中也是如此。(当然,在初始化程序中使用它们存在争议,但这是一个单独的问题。)
没有枯燥的回答。但是,请记住,过早的优化是不好的。在 Mac 或 iPhone 上的 Cocoa 中,访问器/属性的使用必须符合 KVO。KVO 一致性对于 Core Data 和 Cocoa Bindings 自动运行是必要的。在 Core Data 中,不仅需要在修改属性时确保 KVO,在访问它们时也需要。
当您想确保属性内存管理行为时,最好使用访问器/属性,也就是说,始终在设置 ivar 以使用 setter 或点表示法时,并且根据您遵循的内存管理模式,始终使用获取 ivar 时的访问器/属性。
有许多不同的内存管理模式。所有未损坏的对象都确保访问器返回的对象将至少存活到当前自动释放范围的末尾。意思是,对象在当前自动释放范围内被显式保留和自动释放。Apple 推荐的方法在 getter 中明确执行此操作:
- (id)foo {return [[foo retain] autorelease]; }
- (void)setFoo:(id)aFoo {
if(! [aFoo isEqual:foo]) {
[foo release];
foo = [aFoo retain];
}
}
暗示这是他们在合成访问器中遵循的模式。就个人而言,我更喜欢在 setter 中自动释放:
- (id)foo {return foo;}
- (void)setFoo:(id)aFoo {
[foo autorelease];
foo = [aFoo retain];
}
这会在用新值替换旧值之前自动释放旧值。这与 getter 中的保留和自动释放具有完全相同的效果,但需要将对象仅添加到自动释放池一次。大多数情况下,它的保留计数为 1 并且不会自动释放,因此无论发生什么它都不会去任何地方。如果在某些仍然保留它的代码(例如在委托回调中)期间替换了该属性,它不会从它下面消失。
这意味着使用访问器/属性将使您确信您的对象将存在,只要您需要它们,而无需其他代码部分将它们从您的下方释放出来。
始终使用访问器/属性的最后一个也是最好的理由是它为每个属性做了一个更少的假设:该属性有一个基础 ivar,并且它具有相同的名称(我猜这是两个假设)。也许将来您会想用派生访问器替换 ivar。属性符号仍然有效。也许您想重新命名 ivar;该物业仍然可以使用。幸运的是,通常可以依赖 Xcode 中的重构,但为什么还要费心呢?
面向对象编程的重点是使用类上定义的接口。一个类没有充分的理由(尽管有许多偏见和合理化的理由)忽略它自己的接口。在一般情况下,除了访问器本身之外的每个方法都应该将对象视为神圣不可侵犯,并将其内部状态视为私有。编写每个方法,就好像它在一个类别或一个子类中一样,并将 ivars 视为私有状态,除非特定设计需要指示您不这样做。直接访问 ivars 有很多充分的理由,但它们是根据具体情况确定的。