我有一个问题与使用队列更新主线程上的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,甚至可以卸载它......
由于
答案 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
的例子;如果您的单元格显示一次并通过网络向图像发送请求怎么办?但是当块正在等待图像时,单元格会被重用?现在有第二个块参考该单元试图设置其图像。那么,现在你有一场比赛,失败者将决定显示什么图像。