先看代码
这是一段异步下载图片并更新UI的代码
@interface GXAlertView : UIView {
@private
UIImageView *_imageView;
UIActivityIndicatorView *_indicatorView;
}
- (void)asyncLoadUrl:(NSString *)aUrl
{
NSURL *imageURL = [NSURL URLWithString:aUrl];
[imageURL retain];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_async(queue, ^{
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
[imageURL release];
[imageData retain];
dispatch_sync(dispatch_get_main_queue(), ^{
if (imageData) {
_imageView.image = [UIImage imageWithData:imageData];
[imageData release];
}
[_indicatorView stopAnimating];
[_indicatorView removeFromSuperview];
[_indicatorView release];
_indicatorView = nil;
});
});
}
跑一下看看也正常,不过,你要是就以为ok了,那就麻烦了。
崩溃了!!!!
XCode永远停在了XXXView的dealloc里,这是为什么呢?看下调用栈就明白了。没有想到啊,果然是在后台线程的block成了压垮了GXAlertView某个对象的最后一根稻草(retainCount == 0)。至于为什么crash, log说得很清楚
怎么办
解决办法很直接,别访问self就行了(访问实例成员变量和函数会隐含访问self)
- (void)asyncLoadUrl:(NSString *)aUrl
{
NSURL *imageURL = [NSURL URLWithString:aUrl];
[imageURL retain];
// changes. here
UIImageView *imageView = [_imageView retain];
UIActivityIndicatorView *indicatorView = [_indicatorView retain];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_async(queue, ^{
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
[imageURL release];
[imageData retain];
dispatch_sync(dispatch_get_main_queue(), ^{
if (imageData) {
imageView.image = [UIImage imageWithData:imageData];
[imageData release];
}
[indicatorView stopAnimating];
[indicatorView removeFromSuperview];
[indicatorView release];
[imageView release];
});
});
}
小结
block访问self会增加self的引用计数。
所以的UIKit操作都最好都放到主线程去。
release view也算访问UIKit。
在后台线程直接访问UIKit太危险。