IOS後臺線程更新UI的一個陷阱

先看代碼

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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章