Проблема нехватки памяти при загрузке нескольких файлов с использованием AFNetworking
В моем приложении я пытаюсь загрузить тысячи изображений (каждое изображение размером не более 3 МБ) и 10 видеороликов (каждое видео размером не более 100 МБ) и сохранить его в каталоге документов.
Для достижения этого я использую AFNetworking
Здесь моя проблема в том, что я получаю все данные успешно, когда я использую медленный Wi-Fi (около 4 Мбит / с), но та же самая загрузка, если я делаю под Wi-Fi со скоростью 100 Мбит / с, приложение получаетпредупреждение памяти при загрузке изображений и памяти проблема с загрузкой видео, а затем происходит сбой приложения.
-(void) AddVideoIntoDocument :(NSString *)name :(NSString *)urlAddress{
NSMutableURLRequest *theRequest=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlAddress]];
[theRequest setTimeoutInterval:1000.0];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:theRequest];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:name];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"Successfully downloaded file to %@", path);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@", error);
}];
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
//NSLog(@"Download = %f", (float)totalBytesRead / totalBytesExpectedToRead);
}];
[operation start];
}
-(void)downloadRequestedImage : (NSString *)imageURL :(NSInteger) type :(NSString *)imgName{
NSMutableURLRequest *theRequest=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:imageURL]];
[theRequest setTimeoutInterval:10000.0];
AFHTTPRequestOperation *posterOperation = [[AFHTTPRequestOperation alloc] initWithRequest:theRequest];
posterOperation.responseSerializer = [AFImageResponseSerializer serializer];
[posterOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
//NSLog(@"Response: %@", responseObject);
UIImage *secImg = responseObject;
if(type == 1) { // Delete the image from DB
[self removeImage:imgName];
}
[self AddImageIntoDocument:secImg :imgName];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Image request failed with error: %@", error);
}];
[posterOperation start];
}
Приведенный выше код я зацикливаюсь в зависимости от количества видео и изображений, которые я должен загрузить
В чем причина такого поведения
У меня даже есть снимки экрана с выделением памяти для обоих сценариев
Пожалуйста помоги
Добавление кода для сохранения загруженных изображений также
-(void)AddImageIntoDocument :(UIImage *)img :(NSString *)str{
if(img) {
NSData *pngData = UIImageJPEGRepresentation(img, 0.4);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *filePathName =[[paths objectAtIndex:0]stringByAppendingPathComponent:str];
[pngData writeToFile:filePathName atomically:YES];
}
else {
NSLog(@"Network Error while downloading the image!!! Please try again.");
}
}
2 ответа
Причиной такого поведения является то, что вы загружаете свои большие файлы в память (и, вероятно, это происходит достаточно быстро, чтобы у вашего приложения не было возможности отвечать на уведомления о нехватке памяти).
Вы можете уменьшить это, контролируя пиковое использование памяти, не загружая эти загрузки в память. При загрузке больших файлов часто лучше передавать их напрямую в постоянное хранилище. Чтобы сделать это с AFNetworking, вы можете установить outputStream
из AFURLConnectionOperation
и он должен передавать содержимое непосредственно в этот файл, например
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *path = [documentsPath stringByAppendingPathComponent:[url lastPathComponent]]; // use whatever path is appropriate for your app
operation.outputStream = [[NSOutputStream alloc] initToFileAtPath:path append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"successful");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"failure: %@", error);
}];
[self.downloadQueue addOperation:operation];
Кстати, вы заметите, что я не просто звоню start
на эти запросы. Лично я всегда добавляю их в очередь, для которой я указал максимальное количество одновременных операций:
self.downloadQueue = [[NSOperationQueue alloc] init];
self.downloadQueue.maxConcurrentOperationCount = 4;
self.downloadQueue.name = @"com.domain.app.downloadQueue";
Я думаю, что это менее критично в отношении использования памяти, чем передача результатов непосредственно в outputStream
использование постоянного хранилища, но я считаю, что это еще один механизм управления системными ресурсами при инициировании множества одновременных запросов.
Вы можете начать использовать NSURLSession
Скачал задачу.
Я думаю, что это решит вашу проблему.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://someSite.com/somefile.zip"]];
[[NSURLSession sharedSession] downloadTaskWithRequest:request
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error)
{
// Use location (it's file URL in your system)
}];