22

在我的 init 方法中使用点表示法将保留属性初始化为 nil 是不是一个坏主意?

对于像这样的任何普通属性:

@property (nonatomic, retain) id foo;

在我设置的 init 方法中说self.foo = nil。合成方法首先发布或自动发布foo(不完全确定底层实现)。foo在第一个 setter 或 getter 调用之前保证为 nil 吗?或者它会指向随机垃圾,除非我在foo = nil没有点符号的情况下明确设置?

4

1 回答 1

77

在我的 init 方法中使用点表示法将保留属性初始化为 nil 是不是一个坏主意?

是的,这是个坏主意。

alloc1)对象在+序列中已经被清零了init,所以没有必要给它赋值nil。换句话说,这个调用是没有用的,除非你的访问器中有副作用(在这个阶段也应该避免访问器中的副作用)。

2) 您不应使用在部分构造状态(例如initdealloc)中被覆盖的方法向 self 发送消息。

#2有原因吗?我经常做 self.array = [NSMutableArray array]; 在我的初始化方法中。

原因是您的对象不应该对部分构造状态(init...deallocfinalize和许多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.

如果UseItTheRightWaytrue,我们会得到结果:

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.

如果UseItTheRightWayfalse,我们会得到结果:

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]退出之前使用过。这不好=\

不平凡的设计只会产生一堆不受欢迎的副作用、奇怪的行为、泄漏等等。复杂的设计会以非常明显和非常微妙的方式失败,这很难追踪。最好避免因此类琐碎的书面差异而导致维护头痛,并从一开始就以正确的方式编写类(即使您可以在相当长的一段时间内避免这样做,而没有明显的副作用)。

于 2011-05-09T05:04:56.230 回答