Постоянно растущее выделение памяти при загрузке изображений через HTTP в iOS - PullRequest
14 голосов
/ 07 февраля 2011

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

До сих пор я пробовал следующие варианты:

  • Извлечение изображений с использованием синхронного NSURLConnection в NSOperation
  • Извлечениеизображения с использованием асинхронного NSURLConnection в NSOperation
  • Извлечение изображений с помощью [NSData dataWithContentsOfURL: url] в главном потоке
  • Извлечение изображений с использованием синхронного ASIHTTPRequest в рамках NSOperation
  • Извлечение изображений с использованием асинхронного ASIHTTPRequest и добавление его в NSOperationQueue
  • Извлечение изображений с использованием асинхронного ASIHTTPRequest и использование завершениеBlock

Дерево вызовов в Instrumetns показывает, что память используется во время обработкиHTTP-ответ.В случае асинхронного NSURLConnection это находится в

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
}  

В случае синхронного NSURLConnection Instruments показывает растущую запись CFData (хранилище).Проблема с ASIHTTPRequest кажется такой же, как и с асинхронным NSURLConnection в аналогичной позиции кода.Подход [NSData dataWithContentsOfURL: url] показывает увеличение общего объема выделяемой памяти именно в этом операторе.

Я использую NSAutoReleasePool, когда запрос выполняется в отдельном потоке, и я попытался освободить память с помощью[[NSURLCache sharedURLCache] removeAllCachedResponses] - безуспешно.

Есть идеи / советы по решению проблемы?Спасибо.

Редактировать: Поведение отображается только при сохранении изображений с использованием CoreData.Вот код, который я запускаю как NSInvocationOperation:

-(void) _fetchAndSave:(NSString*) imageId {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *url = [NSString stringWithFormat:@"%@%@", kImageUrl, imageId];
HTTPResponse *response = [SimpleHTTPClient GET:url headerOrNil:nil];
NSData *data = [response payload];

if(data && [data length] > 0) {
    UIImage *thumbnailImage = [UIImage imageWithData:data];
    NSData *thumbnailData = UIImageJPEGRepresentation([thumbnailImage scaleToSize:CGSizeMake(55, 53)], 0.5); // UIImagePNGRepresentation(thumbnail); 

    [self performSelectorOnMainThread:@selector(_save:) withObject:[NSArray arrayWithObjects:imageId, data, thumbnailData, nil] waitUntilDone:NO];
}
[pool release];
}

Все связанные с CoreData вещи выполняются в основном потоке, поэтому не должно быть никаких проблем многопоточности CoreData.Однако, если я сохраню изображения, Instruments показывает постоянно увеличивающиеся выделения памяти в позициях, описанных выше.

Редактировать II:

Код, связанный с CoreData:

-(void) _save:(NSArray*)args {
NSString *imageId = [args objectAtIndex:0];
NSData *data = [args objectAtIndex:1];
NSData *thumbnailData = [args objectAtIndex:2];

Image *image = (Image*)[[CoreDataHelper sharedSingleton] createObject:@Image];
image.timestamp =  [NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]];
image.data = data;

Thumbnail *thumbnail = (Thumbnail*)[[CoreDataHelper sharedSingleton] createObject:@"Thumbnail"];
thumbnail.data = thumbnailData;
thumbnail.timestamp = image.timestamp;
[[CoreDataHelper sharedSingleton] save];
}

Из CoreDataHelper (self.managedObjectContext выбирает NSManagedObjectContext, используемый в текущем потоке):

-(NSManagedObject *) createObject:(NSString *) entityName {
return [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self.managedObjectContext];
}

Ответы [ 2 ]

9 голосов
/ 01 марта 2012

У нас была похожая проблема. При извлечении большого количества изображений через http наблюдался огромный рост и пилообразная схема распределения памяти. Мы увидим, как система очистится, более или менее, по ходу дела, но медленно, и не предсказуемо. Тем временем загружались потоки, накапливались все, что удерживало память. Распределение памяти может составить около 200 млн., И тогда мы умрем.

Проблема была в NSURLCache. Вы заявили, что пытались [[NSURLCache sharedURLCache] removeAllCachedResponses]. Мы тоже это попробовали, но потом попробовали что-то немного другое.

Наши загрузки выполняются группами N изображений / фильмов, где N обычно составляет от 50 до 500. Было важно, чтобы мы получили все N как атомная операция.

Прежде чем мы начали нашу группу загрузок http, мы сделали это:

NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:0];
[NSURLCache setSharedURLCache:sharedCache];

Затем мы получаем каждое изображение в N через http с синхронным вызовом. Мы выполняем эту групповую загрузку в NSOperation, поэтому мы не блокируем пользовательский интерфейс.

NSData *movieReferenceData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; 

Наконец, после каждой отдельной загрузки изображения и после того, как мы закончили с нашим объектом NSData для этого изображения, мы вызываем:

[sharedCache removeAllCachedResponses]; 

Наше пиковое поведение при распределении памяти упало до очень удобной горстки мегабайт и перестало расти.

0 голосов
/ 07 февраля 2011

В этом случае вы видите именно то, что должны видеть.-[NSMutableData appendData:] увеличивает размер своего внутреннего буфера для хранения новых данных.Поскольку NSMutableData всегда находится в памяти, это вызывает соответствующее увеличение использования памяти.Чего вы ожидали?

Если конечный пункт назначения для этих изображений находится на диске, попробуйте использовать NSOutputStream вместо NSMutableData.Если затем вы хотите отобразить изображение, вы можете создать UIImage, указывающий на файл, когда вы закончите.

...