Valid use of accessors in init and dealloc methods?
我现在从几个来源(stackoverflow.com,cocoa-dev,文档,博客等)中听到,在init和dealloc方法中使用访问器和设置(foo,setFoo :)是"错误的"。据我所知,如果你这样做,很可能会混淆其他正在观察财产的物体。 (这里给出一个简单的例子)
但是,由于以下原因,我不得不说我不同意这种做法:
新的Objective-C运行时(iPhone上的那个和10.5中的64位运行时)允许您在不声明相应的ivar的情况下声明属性。例如,以下类将在10.5或iPhone(设备,而不是模拟器)上编译得很好:
1 2 3 4 5 6 7 8 9 10 11
| @interface Foo : NSObject { }
@property (retain) id someObject;
@end
@implementation Foo
@synthesize someObject;
@end |
理解上面是一个完全有效的Objective-C类,假设我决定编写一个初始化程序,并且出于内存管理的目的,使用dealloc方法(因为在iPhone上没有GC)。我读过有关初始化器和释放器的所有内容都会让我编写以下两种方法:
1 2 3 4 5 6 7 8 9 10 11 12 13
| - (id) init {
if (self = [super init]) {
//initialize the value of someObject to nil
[self setSomeObject:nil];
}
return self;
}
- (void) dealloc {
//setting someObject to nil will release the previous value
[self setSomeObject:nil];
[super dealloc];
} |
但是,根据文件和流行的观点,这是"错误的"。所以我的问题是这样的:
如何在不使用访问器的情况下初始化someObject?您可能会说编译器(或运行时或其他)将确保someObject已设置为nil,但我相信依赖它会是不正确的行为。在C语言中有一个不错的背景,由于没有正确初始化变量,我看到了相当数量的错误,这似乎没有什么不同。
如果我不应该在dealloc方法中使用访问器,我该如何释放someObject?
如果其中任何一个的答案是"你不能",那么在init和dealloc方法中使用访问器怎么可能不好?
-
合成属性存储可用于Leopard及更高版本的64位应用程序。 它不是10.6的东西。
-
@Jim谢谢,有点滑倒了我的脑海。 我编辑了这个问题。
编辑(2013年2月13日):正如我在下面的评论中所指出的那样,特别是在增加ARC之后,我已经改变了主意。在ARC之前,由于init中的ivar分配不正确,我看到了很多导致崩溃的错误。 IMO,特别是与初级团队合作,使用init中的访问器的罕见问题被ivar访问的常见错误所抵消。由于ARC消除了这些类型的错误,使用init中的访问器可能导致的罕见但可能的错误更重要,因此我转而支持在init和dealloc中直接使用ivars ,只在那些地方;其他地方的访问器是可能的(显然你不能在访问器本身内部使用访问器....)
PRE-ARC回答
我强烈反对那些反对-init中的访问者的人。几乎在所有情况下,这都是使用访问器的一个非常好的地方,并且它节省了我在新的Cocoa编码器中看到的很多错误,这些错误在-init中分配时总是无法保留。
-dealloc是一个更强硬的电话。我有自然倾向于在那里使用访问器(以便它们在任何地方使用),但它可能会因KVO而导致头痛(如果你在你的setter中发布更改通知,甚至可能会导致NSNotifications)。也就是说,虽然我不在-dealloc中使用访问器,但我认为这是非常有争议的,Apple对此非常不一致(我们知道他们在UIViewController的-dealloc中调用setView:)。
在任何情况下,我都会说访问器的使用不足已导致100倍的过度使用错误。我总是错误地使用它们,除非有充分的理由不这样做。
-
我认为在init / dealloc中保持均衡保留/释放的好处很重要,这就是我在init中使用访问器的一个原因。
-
如果在init中分配了每个需要保留的ivar,则只能获得此平衡。在init之后使ivars保持为零是很常见的(懒惰加载对象尤其如此,并且是非常重要的性能优化)。所以我不相信你可以在不禁止重要实施方法的情况下实际保持这种平衡。
-
一年后,我的职位变得柔和了。在-init中存在访问者的危险。如果子类重写访问器并且超类-init调用它,则该对象可能处于不一致状态。问题是,不可能知道你的子类是否会这样做。这是否真的可能取决于您的环境以及是否有不同的组编写超类和子类。制定此规则可以大大减少我们的jr开发人员之间的崩溃,我们从未遇到过上述问题。但是可能存在危险。
-
我知道这个线程已经空闲了很长一段时间,但值得注意的是,即使属性的内存管理语义发生变化,访问器也可以帮助维护代码的有效性。这主要与dealloc相关,其中self.var = nil;即使分配var而未保留也有效,而[var release];则不会。
-
@Rob Napier关于对象的不一致性,Id认为对象在分配后应始终处于半一致状态。换句话说,如果每个对象的任何属性都是零初始化的,那么它们的行为应该合理。如果这不成立,那就是在init中使用访问器的唯一问题,因为构造比C ++更强大,其中子类可以处于完全未定义的状态。
-
@justin和@rob赚大钱......如果我有ivar的属性访问器,我总是使用self.thing = nil;在dealloc中,如果保留它会照顾它,如果它被指定它也会照顾它。我非常警惕那些在dealloc上盲目地叫伊娃的人。
据我所知,合成的ivars不能直接访问的当前10.5行为被Apple认为是一个bug;你应该能够直接访问它,但不能。
因此,你应该能够做到:
代替
与此同时,直接使用访问器是唯一的方法,无需提供明确的ivar。
更新:此错误已得到修复;你现在可以做someObject = nil就好了。
-
有趣。在Xcode中玩了一下之后,看起来你可以直接访问合成的ivar。整齐。
-
(很久以后)只是向我指出,文档说在合成的ivars的情况下,你应该使用-dealloc方法中的setter。对不起,但这意味着我必须将已接受的答案授予Rob。 developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/
-
不,它并没有说你应该使用访问器,它说因为你无法直接访问ivars,你别无选择,只能这样做。过去就是这种情况,但在最新版本的Dev Tools中,您可以直接访问ivar。