iOS崩溃 - 异步UIImageView setImage EXC_BAD_ACCESS

时间:2013-09-10 16:34:01

标签: ios objective-c asynchronous crash uiimageview

我没有在测试中遇到任何崩溃,但我从iTunesConnect收到了一些崩溃报告,如下所示:

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x45d319f8
Crashed Thread:  0

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libobjc.A.dylib                 0x3a6595be objc_msgSend + 30
1   UIKit                           0x34796e30 -[UIImageView setImage:] + 116
2   My App                      0x000c40b2 -[AsyncImageView setImage:] (AsyncImageView.m:224)
3   My App                      0x000c3950 __47-[AsyncImageView loadImageWithURL:animated:]_block_invoke_2 (AsyncImageView.m:147)

AsyncImageView是一个典型的UIImageView子类,它从URL异步加载图像。

以下是资产加载代码,其中包含违规行号:

- (void)loadImageWithURL:(NSURL *)url animated:(BOOL)animated {

    if (url == nil) {
        [self setImage:nil];
        return;
    }

    self.imageAsset = [[Asset alloc] init];
    self.imageAsset.assetURL = url;

    AssetRequest *request = [[AssetRequest alloc] init];
    request.assetURL = url;

    __weak AsyncImageView *weakSelf = self;
    self.assetLoader = [AssetLoader AssetLoaderWithRequest:request
                                                 completion:^(Asset *asset){
                                                    dispatch_async(dispatch_get_main_queue(), ^{

                                                    if (weakSelf.imageAsset.assetURL == asset.assetURL) {

                                                             weakSelf.imageAsset = asset;

                                                             if (animated) {
                                                                 CATransition *transition = [CATransition animation];
                                                                 transition.type = kCATransitionFade;
                                                                 transition.duration = 0.20;
                                                                 [weakSelf.layer addAnimation:transition forKey:nil];
                                                             }

                                                            [weakSelf setImage:weakSelf.imageAsset.assetImage]; //THIS IS LINE 147


                                                             [weakSelf setDisplayLoadingIndicator:NO];
                                                             [weakSelf stopAnimating];
                                                    }

                                                    });
                                                 }
                                                     error:^(NSError *err){

                                                         if (weakSelf.failedToLoad)
                                                             weakSelf.failedToLoad(url);

                                                     }];

    [self.assetLoader load];
}

以下是设置图像的位置,并显示违规行号:

- (void)setImage:(UIImage *)image {

        if (image) {
            [super setImage:image]; //THIS IS LINE 224
            [self hidePlaceholderView];

            if (self.imageLoadedBlock)
                self.imageLoadedBlock();
        }
        else {
            [self showPlaceholderView];
        }

    }

崩溃报告指示设置图像时发生崩溃。有没有明显的理由可能会发生这种情况?或者我可以做任何进一步的错误检查(我已经检查过该图像不为空)?而且,这不会一直发生,只会偶尔发生一次。

2 个答案:

答案 0 :(得分:0)

问题是你正在四处寻找保留周期:

    __weak AsyncImageView *weakSelf = self;

使用这种方法它不会被一个块保留 - 所以如果有人在执行块之前离开足够快 - selfBlock会被释放。

最好取消对dealloc或至少设置完成块的所有操作为“nil”。

- (void) dealloc
{
    [self.assetLoader cancelAllOperations]; //of course you need to implement that
    [super dealloc];
}

我认为如果你对应用程序进行压力测试并在边缘连接上使用它并在响应到来之前快速离开视图,我认为你将能够重现它。

另一个更简单的解决方案是不将“AssetLoader”保留为属性,并将此__block变量设置为远程,以使块保留“self”。但是当视图不在屏幕上并且会更新时,它可能会导致意外行为。

答案 1 :(得分:0)

我研究了SDWebImage源代码,他们在资产加载器完成时做了类似的事情:

if (!weakSelf) {
   return;
}

dispatch_async(dispatch_get_main_queue(), ^{
   if (weakSelf.imageAsset.assetURL == asset.assetURL) {
      weakSelf.imageAsset = asset;
      [weakSelf setImage:weakSelf.imageAsset.assetImage];
       ...
    }                                                         
 });

所以基本上如果weakSelf不再存在则不做任何事情。似乎有点奇怪,问自己它是否仍然存在,但这会解决问题吗?