2013-09-10 4 views
2

Я не испытывал никаких сбоев в тестировании, но я получил несколько сообщений о падениях из iTunesConnect, которые выглядят следующим образом:IOS Crash - Асинхронный UIImageView setImage EXC_BAD_ACCESS

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]; 
     } 

    } 

Отчет аварии указывает, что авария возникает при настройке изображения. Есть ли очевидная причина, почему это может произойти? Или любая дальнейшая проверка ошибок, которую я могу сделать (я уже проверяю, что изображение не является нулевым)? И снова это происходит не всегда, только время от времени.

+0

И все же кажется, что изображение на самом деле не изображение - может быть, оно нулевое/ноль или повреждено. –

+0

Еще лучше, попробуйте SDWebImage. –

+0

Свершился ли он * без * проверки нуля в setImage? Это выглядит подозрительно для меня, тем более что он отключает nil'ing (то есть: удаление) изображения. – Eiko

ответ

0

Проблема заключается в использовании прогулку вокруг сохранить цикл:

__weak AsyncImageView *weakSelf = self; 

При таком подходе не удерживаемой блок - так что если кто-то оставляет достаточно быстро, прежде чем блок выполнен - ​​selfBlock будет освобождён.

Лучше всего отменить все операции над dealloc или, по крайней мере, установить блок завершения на «nil».

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

Я думаю, что вы будете в состоянии воспроизвести, что если вы стресс-тест приложения и играть с ним по краю связи и оставить вид очень быстро, прежде чем приходит ответ.

Другим более простым решением является не сохранять свойство «AssetLoader» как свойство и удалять эту переменную, пропускающую эту переменную __block, чтобы сохранить «я». Но это может привести к неожиданному поведению, когда вид не отображается на экране и будет обновляться.

+0

Я попытался использовать соединение Edge, но не смог его свернуть. Пожалуйста, см. Мой ответ и посмотрите, есть ли у вас какие-либо мысли. – soleil

+0

Могу ли я просто установить self.assetLoader = nil в методе dealloc? – soleil

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 больше не существует, ничего не делать. Кажется немного странным спросить себя, если он все еще существует, но устранит ли это проблему?

+0

Если есть проблема освобождения «weakSelf», это не поможет вам - это указатель на него, будет указывать в любом случае на одну и ту же память - просто объекта нет. Он просто рухнет, если (! WeakSelf), а не позже. –

Смежные вопросы