在dispatch_get_main_queue()上更新UI

xge*_*86x 9 grand-central-dispatch ios

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

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

 dispatch_async(t_queue, ^{
   //load image
   //dispatch_async(dispatch_get_main_queue(),^{
     cell.imageView = image;
   }
 });
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢

NJo*_*nes 14

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

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

@implementation SayHello
-(void)sayHello{
    NSLog(@"Hello");
}
-(void)dealloc{
    NSLog(@"SayHello dead");
}
@end
Run Code Online (Sandbox Code Playgroud)

显然,这个类旨在为我提供一个函数来调用block(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];
    });
});
Run Code Online (Sandbox Code Playgroud)

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

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