Постоянно растущее распределение памяти при извлечении изображений через 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
, указывая на файл, когда закончите.