我的块没有保留它的一些对象

时间:2011-01-03 20:14:03

标签: iphone objective-c ios4 objective-c-blocks

来自Blocks文档:

  

在参考计数环境中,通过   引用时默认值   它中的Objective-C对象   保留。即使你这样也是如此   只需引用一个实例变量   对象。

我正在尝试实现一个完成处理程序模式,其中在执行工作之前将一个块赋予对象,并且在执行工作之后由接收器执行该块。因为我是一个好的记忆公民,所以该块应该拥有它在完成处理程序中引用的对象,然后当块超出范围时它们将被释放。我知道我必须copy将块移动到堆中,因为块将在声明它的堆栈范围内存活。

然而,我的一个对象意外地被解除分配。在一些游戏之后,当块被复制到堆中时,似乎不保留某些对象,而其他对象是。我不确定我做错了什么。这是我可以生成的最小的测试用例:

typedef void (^ActionBlock)(UIView*);

在某种方法的范围内:

NSObject *o = [[[NSObject alloc] init] autorelease];
mailViewController = [[[MFMailComposeViewController alloc] init] autorelease];
NSLog(@"o's retain count is %d",[o retainCount]);
NSLog(@"mailViewController's retain count is %d",[mailViewController retainCount]);
ActionBlock myBlock = ^(UIView *view) {
       [mailViewController setCcRecipients:[NSArray arrayWithObjects:@"test@recipient.com",nil]];
       [o class];
    };
NSLog(@"mailViewController's retain count after the block is %d",[mailViewController retainCount]);
NSLog(@"o's retain count after the block is %d",[o retainCount]);
Block_copy(myBlock);
NSLog(@"o's retain count after the copy is %d",[o retainCount]);
NSLog(@"mailViewController's retain count after the copy is %d",[mailViewController retainCount]);

我希望块在某个时刻保留两个对象,我当然希望它们的保留计数相同。相反,我得到了这个输出:

o's retain count is 1
mailViewController's retain count is 1
mailViewController's retain count after the block is 1
o's retain count after the block is 1
o's retain count after the copy is 2
mailViewController's retain count after the copy is 1

oNSObject的子类)正确保留,不会超出范围。但是,mailViewController不会被保留,并且会在块运行之前被释放,从而导致崩溃。

3 个答案:

答案 0 :(得分:5)

不要使用-retainCount。

对象的绝对保留计数毫无意义。

您应该调用release与导致保留对象完全相同的次数。不会少(除非你喜欢泄漏),当然,没有更多(除非你喜欢崩溃)。

有关详细信息,请参阅Memory Management Guidelines

(来自@ bbum的回答之一)


关于你的问题:

你真的在观察撞车吗?或者你只是盲目地从臀部拍摄并认为它可能会崩溃?

根据您发布的代码,mailViewController似乎是一个实例变量,在这种情况下,块将保留self而不是实例变量。而且,由于您autoreleased是您的实例变量,因此NSAutoreleasePool会像您期望的那样对其进行清理。

总结如下:

  1. 请勿使用-retainCount
  2. 在此回合的运行循环之后,不要存在autorelease个实例变量。
  3. 修改进一步澄清:

    以下是创建块时会发生的事情:

    1. 检查其范围内的对象引用。有两个:self->mailViewControllero
    2. self->mailViewController是结构(self)的成员,因此不会直接保留。保留self代替。
    3. o是一个局部变量。保留它。
    4. 这是正确的行为。至于你的代码......

        使用+0保留计数创建
      1. o 使用+0保留计数
      2. 创建
      3. self->mailViewController 使用+0保留计数创建
      4. myBlocko现在有+1 RC,self也是如此。 self->mailViewController仍然有+0 RC
      5. myBlock已被复制=> +1保留计数
      6. 快进到此运行循环周期结束。

        1. 当前的自动释放池已耗尽。具有+0保留计数的所有对象都被取消分配,包括self->mailViewControllerself->mailViewController现在指向释放的内存,实际上是垃圾。
        2. 快速执行myBlock执行时的某个未来点

          1. myBlock尝试在self->mailViewController上调用方法。但是,self->mailViewController不再指向有效对象,并且您的应用程序崩溃。
          2. 但是,如果mailViewController不是实例变量,那么我们需要查看更多代码。我认为你所看到的行为极不可能是块运行时的问题,但它是可能的。

答案 1 :(得分:2)

文档不再这么说了。它现在正确地说:

  

在手动引用计数环境中,使用局部变量   当复制块时,块内保留

答案 2 :(得分:-2)

由于“mailViewController”是当前类实例的成员,因此该块实际上保留了“self”。