在我的 init 方法中使用点表示法将保留属性初始化为 nil 是不是一个坏主意?
对于像这样的任何普通属性:
@property (nonatomic, retain) id foo;
在我设置的 init 方法中说self.foo = nil
。合成方法首先发布或自动发布foo
(不完全确定底层实现)。foo
在第一个 setter 或 getter 调用之前保证为 nil 吗?或者它会指向随机垃圾,除非我在foo = nil
没有点符号的情况下明确设置?
在我的 init 方法中使用点表示法将保留属性初始化为 nil 是不是一个坏主意?
对于像这样的任何普通属性:
@property (nonatomic, retain) id foo;
在我设置的 init 方法中说self.foo = nil
。合成方法首先发布或自动发布foo
(不完全确定底层实现)。foo
在第一个 setter 或 getter 调用之前保证为 nil 吗?或者它会指向随机垃圾,除非我在foo = nil
没有点符号的情况下明确设置?
在我的 init 方法中使用点表示法将保留属性初始化为 nil 是不是一个坏主意?
是的,这是个坏主意。
alloc
1)对象在+序列中已经被清零了init
,所以没有必要给它赋值nil。换句话说,这个调用是没有用的,除非你的访问器中有副作用(在这个阶段也应该避免访问器中的副作用)。
2) 您不应使用在部分构造状态(例如init
和dealloc
)中被覆盖的方法向 self 发送消息。
#2有原因吗?我经常做 self.array = [NSMutableArray array]; 在我的初始化方法中。
原因是您的对象不应该对部分构造状态(init...
、dealloc
、finalize
和许多copyWithZone:
实现)期间类接口的行为感兴趣。你的类应该有兴趣正确初始化(如 中)并在不引入副作用的情况下init...
自行清理包括其成员(如中dealloc
) 。
考虑这个示例,您可以将其构建为 OS X 的 Foundation 工具:
#import <Foundation/Foundation.h>
enum { UseItTheRightWay = true -OR- false };
@interface MONObjectA : NSObject
{
NSMutableArray * array;
}
@property (nonatomic, retain) NSArray * array;
@end
@implementation MONObjectA
@synthesize array;
- (id)init
{
self = [super init];
if (0 != self) {
NSLog(@"%s, %@",__PRETTY_FUNCTION__, self);
if (UseItTheRightWay) {
array = [NSMutableArray new];
}
else {
self.array = [NSMutableArray array];
}
}
return self;
}
- (void)dealloc
{
NSLog(@"%s, %@",__PRETTY_FUNCTION__, self);
if (UseItTheRightWay) {
[array release], array = nil;
}
else {
self.array = nil;
}
[super dealloc];
}
@end
@interface MONObjectB : MONObjectA
{
NSMutableSet * set;
}
@end
@implementation MONObjectB
- (id)init
{
self = [super init];
if (0 != self) {
NSLog(@"%s, %@",__PRETTY_FUNCTION__, self);
set = [NSMutableSet new];
}
return self;
}
- (void)dealloc
{
NSLog(@"%s, %@",__PRETTY_FUNCTION__, self);
[set release], set = nil;
[super dealloc];
}
- (void)setArray:(NSArray *)arg
{
NSLog(@"%s, %@",__PRETTY_FUNCTION__, self);
NSMutableSet * tmp = arg ? [[NSMutableSet alloc] initWithArray:arg] : nil;
[super setArray:arg];
[set release];
set = tmp;
}
@end
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[[MONObjectB new] release];
/* the tool must be named 'Props' for this to work as expected, or you can just change 'Props' to the executable's name */
system("leaks Props");
[pool drain];
return 0;
}
在此测试中切换行为的主要开关是UseItTheRightWay
.
如果UseItTheRightWay
是true,我们会得到结果:
2011-05-09 01:52:11.175 Props[45138:a0f] -[MONObjectA init], <MONObjectB: 0x10010c750>
2011-05-09 01:52:11.177 Props[45138:a0f] -[MONObjectB init], <MONObjectB: 0x10010c750>
2011-05-09 01:52:11.179 Props[45138:a0f] -[MONObjectB dealloc], <MONObjectB: 0x10010c750>
2011-05-09 01:52:11.179 Props[45138:a0f] -[MONObjectA dealloc], <MONObjectB: 0x10010c750>
leaks Report Version: 2.0
Process: Props [45138]
< --- snip --- >
Process 45138: 1581 nodes malloced for 296 KB
Process 45138: 0 leaks for 0 total leaked bytes.
如果UseItTheRightWay
是false,我们会得到结果:
2011-05-09 01:55:51.611 Props[45206:a0f] -[MONObjectA init], <MONObjectB: 0x10010c750>
2011-05-09 01:55:51.614 Props[45206:a0f] -[MONObjectB setArray:], <MONObjectB: 0x10010c750>
2011-05-09 01:55:51.615 Props[45206:a0f] -[MONObjectB init], <MONObjectB: 0x10010c750>
2011-05-09 01:55:51.617 Props[45206:a0f] -[MONObjectB dealloc], <MONObjectB: 0x10010c750>
2011-05-09 01:55:51.618 Props[45206:a0f] -[MONObjectA dealloc], <MONObjectB: 0x10010c750>
2011-05-09 01:55:51.618 Props[45206:a0f] -[MONObjectB setArray:], <MONObjectB: 0x10010c750>
leaks Report Version: 2.0
Process: Props [45206]
< --- snip --- >
Process 45206: 1585 nodes malloced for 297 KB
Process 45206: 1 leak for 48 total leaked bytes.
Leak: 0x100110970 size=48 zone: DefaultMallocZone_0x100005000 instance of 'NSCFSet', type ObjC, implemented in Foundation
0x70294ff8 0x00007fff 0x00001080 0x00000001 .O)p............
0x00000001 0x00000000 0x00000000 0x00010000 ................
0x707612a8 0x00007fff 0x00000000 0x00000000 ..vp............
问题 #1
这个例子的明显失败是泄漏,在dealloc
.
问题 #2
会咬你的第二件事更微妙:
-[MONObjectA init]
-[MONObjectB setArray:]
-[MONObjectB init]
这是什么???-[MONObjectB setArray:]
之前调用过 -[MONObjectB init]
?这意味着MONObjectB
' 的实现在之前使用过 -[MONObjectB init]
,甚至在-[MONObjectA init]
退出之前使用过。这不好=\
不平凡的设计只会产生一堆不受欢迎的副作用、奇怪的行为、泄漏等等。复杂的设计会以非常明显和非常微妙的方式失败,这很难追踪。最好避免因此类琐碎的书面差异而导致维护头痛,并从一开始就以正确的方式编写类(即使您可以在相当长的一段时间内避免这样做,而没有明显的副作用)。