先看代碼
這是一段異步下載圖片並更新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太危險。