在@mmalc 对这个问题的回答中,他指出“一般来说,您不应该在dealloc(或 init)中使用访问器方法。” 为什么 mmalc 会这样说?
我能想到的唯一真正原因是性能和避免@dynamic setter 的未知副作用。
讨论?
它基本上是一个将错误可能性降至最低的指南。
在这种情况下,您的 setter/getter 可能会无意中对对象的状态做出直接或间接的假设。当对象处于设置或销毁过程中时,这些假设可能会成为问题。
例如,在下面的代码中,观察者不知道“示例”正在被销毁,并且可以假设其他已被释放的属性是有效的。
(您可以争辩说您的对象应该在自行拆除之前移除所有观察者,这是一种很好的做法,也是防止意外问题的另一个准则)。
@implementation Example
-(void) setFoo:(Foo*)foo
{
_foo = foo;
[_observer onPropertyChange:self object:foo];
}
-(void) dealloc
{
...
self.foo = nil;
}
@end
这一切都是关于使用惯用一致的代码。如果您正确地对所有代码进行模式化,则有一组规则可以保证在 init/dealloc 中使用访问器是安全的。
最大的问题是(正如 mmalc 所说)设置属性默认状态的代码不应通过访问器,因为它会导致各种讨厌的问题。问题是 init 没有理由设置属性的默认状态。由于多种原因,我一直在使用自初始化的访问器,如下面的简单示例:
- (NSMutableDictionary *) myMutableDict {
if (!myMutableDict) {
myMutableDict = [[NSMutableDictionary alloc] init];
}
return myMutableDict;
}
这种类型的属性初始化允许延迟许多实际上可能不需要的初始化代码。在上述情况下,init 不负责初始化属性状态,并且在 init 方法中使用访问器是完全安全的(甚至是必要的)。
诚然,这确实对您的代码施加了额外的限制,例如,具有超类中属性的自定义访问器的子类必须调用超类访问器,但这些限制与 Cocoa 中常见的各种其他限制并不不一致。
你是在自问自答:
如果您的类可能是子类,则后者尤其是一个问题。
但是,尚不清楚为什么在Objective-C 2访问器中专门解决了这个问题?无论您是使用声明的属性还是自己编写访问器,都适用相同的原则。
可能是 setter 具有应该运行的逻辑,或者实现使用了名称与 getter/setter 不同的 ivar,或者可能需要释放和/或将其值设置为 nil 的两个 ivar。唯一可靠的方法是调用 setter。setter 有责任以这样一种方式编写,即在 init 或 dealloc 期间调用时不会发生不希望的副作用。
来自“Cocoa 设计模式”,Buck,Yacktman,第 115 页:“...当您使用现代 Objective-C 运行时或...的合成实例变量时,没有其他实用的替代方法可以替代使用访问器。”
事实上,对于一个频繁出现和消失的类(如详细视图控制器),您希望在 init 中使用访问器;否则,您最终可能会在 viewDidUnload 中释放一个您稍后尝试访问的值(它们在 CS193P 中显示...)
您可以通过在分配/解除分配时不调用设置器来创建相同的问题。
我不认为你可以通过在 init/dealloc 中直接使用保留/释放来实现任何目标。您只需更改可能的错误集。
每次您必须考虑财产分配/解除分配的顺序。