2013-05-22 3 views
0

У меня есть этот простой UITableView, и каждая ячейка имеет соответствующее ему изображение. Все, что я делаю, это отображение названия для изображения и самого изображения в каждой ячейке. Вот мой cellForRowAtIndexPath:UITableView с образами сбоев изображений при прокрутке вниз

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 

    // this is where the data for each cell is 
    NSDictionary *dataForThisCell = cachedData.posts[indexPath.row][@"data"]; 

    // this creates a new cell or grabs a recycled one, I put NSLogs in the if statement to make sure they are being recycled, they are. 
    post *cell = (post *) [self.tableView dequeueReusableCellWithIdentifier:@"postWithImage"]; 
    if (cell == nil) { 
     cell = [[[NSBundle mainBundle]loadNibNamed:@"postWithImage" owner:self options:nil]objectAtIndex:0]; 
     [cell styleCell]; 
    } 

    // if this cell has an image we need to stick it in the cell 
    NSString *lowerCaseURL = [dataForThisCell[@"url"] lowercaseString]; 
    if([lowerCaseURL hasSuffix: @"gif"] || [lowerCaseURL hasSuffix: @"bmp"] || [lowerCaseURL hasSuffix: @"jpg"] || [lowerCaseURL hasSuffix: @"png"] || [lowerCaseURL hasSuffix: @"jpeg"]) { 

     // if this cell doesnt have an UIImageView, add one to it. Cells are recycled so this only runs several times 
     if(cell.preview == nil) { 
      cell.preview = [[UIImageView alloc] init]; 
      [cell.contentView addSubview: cell.preview]; 
     } 

     // self.images is an NSMutableDictionary that stores the width and height of images corresponding to cells. 
     // if we dont know the width and height for this cell's image yet then we need to know now to store it 
     // once the image downloads, and then cause our table to reload so that heightForRowAtIndexPath 
     // resizes this cell correctly 
     Boolean shouldReloadData = self.images[dataForThisCell[@"name"]] == nil ? YES : NO; 

     // download image 
     [cell.preview cancelImageRequestOperation]; 
     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString: dataForThisCell[@"url"]]]; 
     [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; 
     [cell.preview setImageWithURLRequest: request 
           placeholderImage: [UIImage imageNamed:@"thumbnailLoading.png"] 
             success: ^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { 

              // if we indicated earlier that we didnt know the dimensions of this image until 
              // just now after its been downloaded, then store the image dimensions in self.images 
              // and tell the table to reload so that heightForRowAtIndexPath 
              // resizes this cell correctly 
              if(shouldReloadData) { 
               NSInteger imageWidth = image.size.width; 
               NSInteger imageHeight = image.size.height; 
               if(imageWidth > [ColumnController columnWidth]) { 
                float ratio = [ColumnController columnWidth]/imageWidth; 
                imageWidth = ratio * imageWidth; 
                imageHeight = ratio* imageHeight; 
               } 
               if(imageHeight > 1024) { 
                float ratio = 1024/imageHeight; 
                imageHeight = ratio * imageHeight; 
                imageWidth = ratio* imageWidth; 
               } 
               self.images[dataForThisCell[@"name"]] = @{ @"width": @(imageWidth), @"height": @(imageHeight), @"titleHeight": @([post heightOfGivenText: dataForThisCell[@"title"]]) }; 
               [self.tableView reloadData]; 

              // otherwise we alreaady knew the dimensions of this image so we can assume 
              // that heightForRowAtIndexPath has already calculated the correct height 
              // for this cell 
              }else{ 

               // assign the image we downloaded to the UIImageView within the cell 
               cell.preview.image = image; 

               // position the image 
               NSInteger width = [self.images[dataForThisCell[@"name"]][@"width"] integerValue]; 
               NSInteger height = [self.images[dataForThisCell[@"name"]][@"height"] integerValue]; 
               cell.preview.frame = CGRectMake(([ColumnController columnWidth] - width)/2 , [self.images[dataForThisCell[@"name"]][@"titleHeight"] integerValue] + 10, width, height); 

              } 

             } 
             failure: ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {}]; 


    } 

    // set title of the cell 
    cell.title.text = [NSString stringWithFormat: @"%@\n\n\n\n\n", dataForThisCell[@"title"]]; 

    `enter code here`// ask for a restyle 
    [cell setNeedsLayout]; 

    // returns my customized cell 
    return cell; 

} 

Что происходит, что все работает точно так же, как я хочу его, однако, как только я прокручиваю вниз мимо около 100 ячеек или так на фоне моего приложения темнеет в течение нескольких секунд, а затем я см. мой рабочий стол (я видел, как некоторые люди называют это HSOD - домашним экраном смерти). Иногда в консоли в xcode я вижу предупреждения памяти перед сбоем, а иногда и нет.

Я знаю, что независимо от проблемы, это связано с помещением изображений в ячейки. Если я закомментировать только эту строку:

cell.preview.image = image; 

Тогда все работает отлично и не врезаться больше (но, конечно, изображения не отображаются в ячейках).

Клетки повторного использования, и я знаю, что это работает, для хорошей меры я установить свойство изображения в UIImageView к нулю:

- (void) prepareForReuse { 
    [super prepareForReuse]; 
    if(self.preview != nil) 
     self.preview.image = nil; 
} 

и в моем AppDelegate я также определить это:

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { 

    [UIImageView clearAFImageCache]; 

} 

Который удаляет кеш изображения, но это также не устраняет проблему (и, в любом случае, iOS должна автоматически очищать кэши изображений от предупреждений памяти).

Я провел анализ на мой проект, и он не сообщает, отсутствие утечек памяти, а вот профайлер, показывающий, что, а также с указанием распределения в момент аварии:

enter image description here

Другие, чем случайное предупреждение о памяти в консоли, которое появляется примерно в 2/3 раза в момент сбоя приложения, никаких других ошибок, появляющихся на консоли, нет, и я не ударяю ни о каких контрольных точках или исключениях.

ответ

0

Я нашел решение, хотя и не идеальный один:

Библиотека AFNetworking является блестящим и Я предполагаю, что причина моей проблемы заключается в моем собственном коде или в том, что я не понимаю, как работает NSCache.

AFNetworking кэширует изображения с использованием NSCache от Apple. NSCache похож на NSMutableDictionary, но выпускает объекты, когда память распределена тонкой (см. Больше here).

В рамках UIImageView + AFNetworking.м я нашел определение

+ (AFImageCache *)af_sharedImageCache 

И изменили его походить на это:

+ (AFImageCache *)af_sharedImageCache { 
    static AFImageCache *_af_imageCache = nil; 
    static dispatch_once_t oncePredicate; 
    dispatch_once(&oncePredicate, ^{ 
     _af_imageCache = [[AFImageCache alloc] init]; 
     _af_imageCache.countLimit = 35; 
    }); 

    return _af_imageCache; 
} 

Важным направлением здесь является

_af_imageCache.countLimit = 35; 

Это говорит объект NSCache который используется в AFNetworking кэшировать изображения, которые должны содержать только до 35 предметов.

По неизвестным мне причинам iOS не автоматически удалял объекты из кеша, как и следовало ожидать, а вызов removeAllObjects в кеше тоже не работал. Это решение вряд ли идеально, потому что оно не может полностью использовать кэш или может использовать кеш, но тем не менее он по-прежнему не позволяет кешу пытаться хранить бесконечное количество объектов.

1

Все эти распределения вы создаете новые ячейки представления таблицы каждый раз, когда они запрашиваются, вместо повторного использования существующих. Без установки reuseIdentifier для ячеек, созданных с UINib, dequeueReusableCellWithIdentifier: всегда будет возвращать `nil.

Чтобы это исправить, добавьте следующий код (как указано in this question):

[self.tableView registerNib:[UINib nibWithNibName:@"nibname" bundle:nil] forCellReuseIdentifier:@"cellIdentifier"];

+0

Я не думаю, что это причина. Кажется, что они переработаны - я поставил NSLog в оператор if, и он работает только 7 раз. – Macmee

+0

Хорошо, тогда это проблема с вашим блоком 'success'. Я бы рекомендовал отображать изображения в ячейках по размеру вашего заполнителя (при необходимости масштабируется с использованием режима заполнения аспект). Не рекомендуется ссылаться на 'self' и манипулировать ячейкой таблицы так, как вы это делаете. – mattt

+0

Я изменил его на этот http://pastebin.com/seBsBZ7j, и он по-прежнему падает после получения 2 предупреждений памяти. – Macmee

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