先看代码
这是一段异步下载图片并更新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太危险。