使用块和ARC导致“发送到解除分配的实例的消息”错误

时间:2012-05-29 18:45:03

标签: ios automatic-ref-counting objective-c-blocks

我遇到了ARC和块的问题,但已经解决了问题。不幸的是,我不知道到底发生了什么,并想了解更多关于我的情况。

最初,我有代码执行此操作

for(__block id<Foo> object in objects) {
    foo download:someParm
         success:^{
            object.state = StateNewState; 
         }
    ];
}

这导致保留不平衡。访问对象并且据说已经解除分配时发生崩溃。我编写了一个实现并使用“copy”属性来创建successBlock属性的类,该属性保存了传递给下载函数的success参数的块。我用以下

替换了代码
for(id<Foo> object in objects) {
    foo download:someParm
         success:^(id<Foo> successObject){
            successObject.state = StateNewState; 
         }
    ];
}

没有更多的解除分配的对象错误,但我还没有运行仪器来检查我是否没有泄漏。一些如何使用__block导致对象被释放太多次,我无法弄清楚为什么。我将继续研究这个问题的原因,但我认为这对其他人来说是一个有趣的问题。

我想值得注意的是,对象数组是一个自动释放的数组,它是在我在本文前面写下的代码的行中创建的。不要认为这很重要,但我想我只是在那里。我在这篇文章中提到的代码不是确切的代码,因为我正在使用它来工作,并且那里有一堆绒毛。但是for循环中没有创建其他对象。

当应用程序崩溃时,它运行下载,然后运行回调,顺便说一句,我正在使用ASIHttp。当我再次尝试下载时,它会运行并且不会调用回调,因为对象已被释放并且委托是无效的。在此之后,当一个字典访问对象时,该字典包含指向我们崩溃的对象的指针。

2 个答案:

答案 0 :(得分:4)

Blocks Programming Topics说:

  

在块中使用实例变量将导致该对象   本身要保留。如果您希望覆盖此行为   特定的对象变量,您可以使用__block存储标记它   类型修饰符。

     

如果您使用ARC,则会在复制块并稍后释放时自动保留和释放对象变量。

因此,如果您不使用__block,我认为您会发现您的变量正在为您保留。例如,这似乎对我有用:

NSMutableArray *array = [[NSMutableArray alloc] init];

// add two custom objects to that array

[array addObject:[[MyObject alloc] initWithText:@"One" number:1]];
[array addObject:[[MyObject alloc] initWithText:@"Two" number:2]];
[array addObject:[[MyObject alloc] initWithText:@"Three" number:3]];

// now replace the text field in each of the three objects with the word "Done"

for (MyObject *object in array)
{
    [self blockTestInvocation:^{
        NSLog(@"%s %@", __FUNCTION__, [NSDate date]);

        object.text = @"Done";
    }];
}

我发现当__block同步调用传递的块时,blockTestInvocation的存在或不存在会产生重大影响,但是当我将其设置为异步调用块时(在我的数组和对象以其他方式被释放之后),__block的缺失确保了对象被保留,从而防止了被发送到解除分配的实例的可怕消息&#34; (也没有泄漏)。但是对于__block,该对象不会被保留,因此可以在代码块尝试引用它时释放它。

答案 1 :(得分:1)

两件事:

1)有些事情并不能说明你所描述的内容。你说你把这些对象放在一个自动释放的数组中,这个数组可能是为了运行循环而临时存储的。然后你的块回调在这些对象上设置一些状态,除非其他东西也保留它们,否则这将是毫无意义的。所以你的问题在于其他东西 - 无论创造什么,这些对象应该在它们需要的时候保留它们 - 大概足以观察状态变化。如果是,您将不会收到EXC_BAD_ACCESS错误。

2)你的循环中不需要__block限定符。它基本上告诉编译器你的块可能会为该引用分配一个新对象,因此它需要取消引用该变量。但你的阻止不会这样做。您只是向对象发送消息。如果不使用__block限定符,则块将获取值的const副本 - 该值是指向对象的指针。然后,当您执行object.state = StateNewState时,您将向该指针处的对象发送setState:newState消息。所以这应该工作正常:

for(id<Foo> object in objects) {
    foo download:someParm
         success:^{
            object.state = StateNewState; 
         }
    ];
}