从完成块中保护我的代码免受僵尸的攻击

时间:2012-02-18 13:44:32

标签: objective-c ios objective-c-blocks grand-central-dispatch

我熟悉委托模式并且委托我的委托,特别是在我的视图控制器消失时进行仍在进行的异步调用时。我没有委托,并且回调成功返回nil对象。

我现在正在尝试使用完成块来使我的代码更容易阅读。

我从视图控制器调用网络服务,并传递一个更新我的UITableView的块。在正常情况下它工作正常。但是,如果我在视图完成之前离开视图,则执行完成处理程序块 - 但是UITableView现在是一个僵尸。

处理此问题的通常模式是什么?

使用代码示例进行更新

这是一个iPad应用程序,我在屏幕上同时有两个视图控制器,就像拆分视图一样。一个是细节,另一个是图像网格。我点击一个图像,它告诉细节加载信息。但是,如果我在他们有机会进行网络通话之前点击图像太快 - 我有问题。在更改图像时,会调用下面的代码来计算图像的最爱....

所以这就是我的困境,如果我使用下面的代码 - 它工作正常,但如果你在网络响应之前切换图像,它会在仪器中泄漏。

如果我删除__block并传入self,那么它会与僵尸一起崩溃。

我无法获胜......我确信我错过了使用积木的基本原则。

__block UITableView *theTable = [self.table retain];
__block IndexedDictionary *tableData = [self.descriptionKeyValues retain];
FavouritesController *favourites = [Container controllerWithClass:FavouritesController.class];
[favourites countFavouritesForPhoto:self.photo 
                         completion:^(int favesCount) {

                             [tableData insertObject:[NSString stringWithFormat:@"%i", favesCount]   
                                              forKey:@"Favourites:" atIndex:1];                 
                             [theTable reloadData];

                             [tableData release];
                             [theTable release];
                         }];

任何提示?感谢

第二次更新

我改变了加载收藏夹的方式。我不是将收藏夹作为单身人物,而是在每次照片更改时创建一个实例。通过替换它并杀死旧的 - 块无处回调(我猜它甚至不存在),我的代码现在看起来像下面的那样,它似乎正在工作:

[self.favourites countFavouritesForPhoto:self.photo 
                         completion:^(int favesCount) {      
                             [self.descriptionKeyValues insertObject:[NSString stringWithFormat:@"%i", favesCount]   
                                              forKey:@"Favourites:" atIndex:1];                 
                             [self.table reloadData];
                         }];

它不会泄漏,也不会崩溃。

3 个答案:

答案 0 :(得分:0)

我建议您在块的开头测试tableview不是nil。当它的父视图离开屏幕时,听起来像tableview被正确地丢弃了,所以在那之后,没有tableview操作是有效的。

在块中保留UITableView是一个坏主意,因为如果tableview不在屏幕上,datasource / tableview更新可能导致隐式方法调用和通知不相关。

答案 1 :(得分:0)

Block将保留它引用的任何对象,但使用__block注释的对象除外。如果您不想执行完成块,只需创建一些属性isCancelled,并在调用完成块之前检查它是否为YES

答案 2 :(得分:0)

所以你有一个后台操作,它必须在完成后回调另一个对象,同时可以销毁该对象。当您有非保留引用时,您描述的崩溃就会发生。您看到的问题是被引用的对象消失而指针无效。通常,你所做的是取消注册dealloc方法中的委托,以便后台任务继续,并且只要它准备好传回结果,就会说“ Shoot,我的回调对象是nil ”,并且至少它没有崩溃。

尽管如此,手动处理弱引用仍然很乏味且容易出错。您可以忘记在dealloc方法中取消委托,并且在遇到代码崩溃的情况之前可能会在几个月内没有通知。

如果你的目标是iOS 5.0,我会阅读ARC及其提供的弱引用。如果您不想使用ARC,或者需要定位5.x之前的设备,我建议使用归零的弱引用库,如MAZeroingWeakRef,它们也适用于3.x设备。

使用ARC的弱引用或MAZeroingWeakRef,您将使用指向表的这些花哨的弱引用对象之一来实现后台任务。现在,如果指向的对象消失,弱指针将自动为止,后台任务不会崩溃。