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

我遇到了ARC和块的问题,但解决了这个问题。 不幸的是,我不知道究竟发生了什么,想了解更多的情况。

原来,我有这样做的代码

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

这导致了保留的不平衡。 一个对象被访问时发生崩溃,并且被认为已经被释放。 我写了一个实现并使用“copy”属性的类来创build一个successBlock属性,该属性保存了传递给下载函数成功参数的块。 我用下面的代码replace了这个代码

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

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

我想可能值得注意的是objects数组是一个autoreleased数组,它是在我之前在这篇文章中写下的代码行中创build的。 不要以为这会很重要,但我认为我只是通过那里。 我放在这篇文章中的代码并不是确切的代码,因为我正在使用这个工作,并且里面有一堆绒毛。 但是for循环中没有创build其他对象。

当应用程序崩溃,它运行下载,然后运行callback,顺便说一句,我使用ASIHttp。 当我试图再次下载它运行和callback不会被调用,因为对象已被释放,委托没有。 之后,当对象被包含指向对象的指针的字典访问时,我们会崩溃。

块编程主题说:

在块内使用实例variables将导致对象本身被保留。 如果您希望覆盖特定对象variables的此行为,则可以使用__block存储types修饰符标记它。

如果您正在使用ARC,则会在复制块并随后发布时自动保留并释放对象variables。

因此,如果你不使用__block ,我想你会发现你的variables正在被保留。 例如,这似乎为我工作:

 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同步调用传递的块产生实质性影响,但当我设置它来asynchronous调用块(在我的数组和对象本来会被释放后)时,没有__block确保对象被保留,从而防止了可怕的“消息发送到解除分配的实例”(也没有泄漏)。 但是使用__block ,对象不会被保留,因此可能会在代码块试图引用它时被释放。

两件事情:

1)有些东西不能说明你所描述的内容。 你说你把这些对象放在一个autoreleased数组中,这大概是临时存储用于运行循环的目的。 然后你的块callback在这些对象上设置了一些状态,这是毫无意义的,除非别的东西也保留它们。 所以你的问题在于别的东西 – 无论创造那些物体应该在需要的时候保留它们 – 大概足够长的时间来观察状态的变化。 如果是的话,你不会得到EXC_BAD_ACCESS错误。

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

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