Постоянно растущее распределение памяти при извлечении изображений через HTTP в iOS

Я внедряю приложение 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 инструменты показывают растущую запись CFData (store). Проблема с ASIHTTPRequest кажется такой же, как и с асинхронным NSURLConnection в аналогичной кодовой позиции. Подход [NSData dataWithContentsOfURL: url] показывает все большее количество общего распределения памяти в этом утверждении.

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

Любые идеи/подсказки для решения проблемы? Спасибо.

Edit: Поведение появляется только в том случае, если я сохраняю изображения с помощью 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 вещи выполняются в Main-Thread здесь, поэтому не должно быть многопоточности CoreData. Однако, если я сохраняю изображения, приборы показывают постоянное увеличение выделения памяти в положениях, описанных выше.

Изменить 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];
}

Ответы

Ответ 1

У нас была аналогичная проблема. Приобретая множество изображений по 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]; 

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

Ответ 2

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

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