我有时会告诉自己。我们的类也经常这样做。但在 Objective-C 中,有几个地方这样做是有风险的:init
和 dealloc
。
本文是Objective-C 中的代码气味系列文章中的一篇。
在 Objective-C 的 init
和 dealloc
代码中,我经常看到这样的代码。我举一个简单的例子。你能找出问题所在吗?
- (id)initWithFoo:(id)foo
{
self = [super init];
if (self)
self.something = foo;
return self;
}
- (void)dealloc
{
self.something = nil;
[super dealloc];
}
提示:是那些self.
。它们容易让人误以为是简单的作业。但请记住,点符号隐藏着信息。
让我们避开点符号,再试一次:
- (id)initWithFoo:(id)foo
{
self = [super init];
if (self)
[self setSomething:foo];
return self;
}
- (void)dealloc
{
[self setSomething:nil];
[super dealloc];
}
现在你看到了吗?
向自己发送信息通常没有问题。但有两个地方要避免:
在这两个时间段,物体处于一种有趣的、介于两者之间的状态。它缺乏完整性。在这两个时间段调用方法是一种代码缺陷。为什么呢?因为每个方法在对对象进行操作时都应保持不变。下面是对象在方法中流动时的自我一致性(self-consistency)概述:
提示:不变性使你保持清醒。
我并没有为此做出非常规的尝试。苹果公司有一份关于实用内存管理的文档,其中有一节的标题是 "不要在初始化方法和 dealloc 中使用访问方法"。
The only places you shouldn’t use accessor methods to set an instance variable are in initializer methods and dealloc
. To initialize a counter object with a number object representing zero, you might implement an init
method as follows:
- init {
self = [super init];
if (self) {
_count = [[NSNumber alloc] initWithInteger:0];
}
return self;
}
To allow a counter to be initialized with a count other than zero, you might implement an initWithCount: method as follows:
- initWithCount:(NSNumber *)startingCount {
self = [super init];
if (self) {
_count = [startingCount copy];
}
return self;
}
Since the Counter class has an object instance variable, you must also implement a dealloc method. It should relinquish ownership of any instance variables by sending them a release message, and ultimately it should invoke super’s implementation:
- (void)dealloc {
[_count release];
[super dealloc];
}
解决方法很简单:在 Objective-C 的 init
和 dealloc
方法中,直接访问实例变量,而不是通过属性。在非 ARC 代码中,检查属性属性是否保留或分配。然后编写与直接访问相匹配的代码。例如,如果某个属性是保留属性,默认支持 ivar _something
,那么我们的代码就会变成:
- (id)initWithFoo:(id)foo
{
self = [super init];
if (self)
_something = [foo retain];
return self;
}
- (void)dealloc
{
[_something release];
[super dealloc];
}
在说过 "避免在 init
和 dealloc
中向 self
发送信息 "之后,我现在想缓和一下这种说法。毕竟有两个地方是可以这样做的:
init
阶段的最后阶段,以及这是因为在这两个地方,对象具有自一致性( self-consistency)。在 init
中,所有 ivars 都已建立。在 dealloc
中,没有一个 ivars 被销毁。
但您仍需谨慎行事,并认识到自己在对象生命周期中的位置。仅仅创建一个对象并不能开始任何繁重的工作。创建和销毁都要轻便快捷。
译自 Jon Reid 的 Objective-C init: Why It’s Helpful to Avoid Messages to self 侵删