Проблема нехватки памяти при загрузке нескольких файлов с использованием 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)
     }];
Другие вопросы по тегам