【问题标题】:Initializing a property, dot notation初始化属性,点表示法
【发布时间】:2011-08-21 10:04:21
【问题描述】:

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

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

@property (nonatomic, retain) id foo;

说在我的初始化方法中我设置了self.foo = nil。合成方法首先释放或自动释放foo(不完全确定底层实现)。在第一个 setter 或 getter 调用之前,foo 是否保证为零?或者它会指向随机垃圾,除非我明确设置 foo = nil 而不使用点符号?

【问题讨论】:

    标签: objective-c initialization properties dealloc reference-counting


    【解决方案1】:

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

    是的,这是个坏主意。

    1) 对象已经在alloc+init 序列中归零,因此不必将其分配为零。换句话说,这个调用是没有用的,除非你的访问器中有副作用(在这个阶段也应该避免访问器中的副作用)。

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

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

    【讨论】:

    • 谢谢。 #2有原因吗?我经常做 self.array = [NSMutableArray array];在我的初始化方法中。
    • 很棒的演示!直到现在才明白其中的含义。
    • @SeanDanzeiser 你能更具体一点吗?你可能会问很多事情
    • 我对此有点陌生,很抱歉不清楚。我只是想知道您是如何为 UseItTheRightWay 的真假创建打印输出的(“我们得到了结果”部分)
    • @SeanDanzeiser 啊,好的。做我在这个例子中所做的事情是非常不寻常的。我这样做是为了让它自成一体。你不应该在生产代码中使用这种方法——它对于很少的概念证明很有用。话虽如此,这不适用于iOS。但是,通常有更多有用的工具可供您使用——尤其是 Instruments->leaks。现在回答你的问题:在main 返回之前查找system("leaks Props"); 的部分——这就像在Terminal.app 中输入leaks SOME_APP_NAME。输出被手动剪断,以保持演示简洁。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-21
    相关资源
    最近更新 更多