在dispatch_get_main_queue()上更新UI

时间:2012-02-01 23:35:07

标签: ios grand-central-dispatch

我有一个问题与使用队列更新主线程上的UI有关。

好吧,假设我们创建了一个UITableView,它显示了一个带有UIImageView的UILabel。 UIImage是在prepareCellfor ..中使用:

异步加载的
 dispatch_async(t_queue, ^{
   //load image
   //dispatch_async(dispatch_get_main_queue(),^{
     cell.imageView = image;
   }
 });

但是当块正在获取图像时,用户按下一个单元格(或导航视图控制器上的后退按钮),为该单元格加载de DetailViewController(或在应用程序中返回)。

我的问题是:当块启动主线程以更新单元格的imageView时会发生什么?它试图更新未加载到窗口上的UIView,甚至可以卸载它......

由于

1 个答案:

答案 0 :(得分:14)

这是一个很好的问题。使用ARC时的答案主要是块本身保留了对象,因此它将在以后出现。这是那些微妙的记忆陷阱之一。如果取消分配此单元格的UITableView并释放其所有单元格,则将保留此单元格(虽然在屏幕外)并且将完成cell.imageView = image;的分配,然后它将被释放。

我是受控实验的忠实粉丝,并开始测试这个,但UITableView有许多活动部分(没有双关语)。所以我使用一个简单的NSObject子类创建了一个非常简单的实验,如下所示:

@implementation SayHello
-(void)sayHello{
    NSLog(@"Hello");
}
-(void)dealloc{
    NSLog(@"SayHello dead");
}
@end

显然,这个类旨在为我提供一个调用块(sayHello)的函数,并在解除分配时生成NSLog

我跑了我的测试:

SayHello *hello = [[SayHello alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    double delayInSeconds = 30.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [hello sayHello];
    });
});

30秒甚至给出了最懒惰的runloop时间来释放“hello”对象(事实上,如果没有保留它)。但对于那些30秒,控制台是沉默的。在30秒过期后,我立即得到“Hello”,接着是“SayHello dead”消息。

那怎么这是一个“陷阱”?嗯,很明显,如果你没有意识到Blocks / ARC正在这样做,它最终可能会保留你认为应该消失的东西。还有你的UITableViewCell的例子;如果您的单元格显示一次并通过网络向图像发送请求怎么办?但是当块正在等待图像时,单元格会被重用?现在有第二个块参考该单元试图设置其图像。那么,现在你有一场比赛,失败者将决定显示什么图像。