Memory pressure issue while downloading multiple f

2019-06-14 05:34发布

In my application i am trying to download thousands of images (each image size with a maximum of 3mb) and 10's of videos (each video size with a maximum of 100mb) and saving it in Documents Directory.

To achieve this i am using AFNetworking

Here my problem is i am getting all the data successfully when i am using a slow wifi (around 4mbps), but the same downloading if i am doing under a wifi with a speed of 100mbps the application is getting memory warning while downloading images and memory pressure issue while downloading videos and then application is crashing.

-(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];
}

The above code i am looping according to the number of videos and images that i have to download

What is the reason behind that behaviour

I even have screen shots of memory allocation for both the scenarios

Please Help

Adding code for saving the downloaded images also

-(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条回答
不美不萌又怎样
2楼-- · 2019-06-14 06:12

The reason for this behavior is that you're loading your large files into memory (and presumably it's happening quickly enough that you app isn't having a chance to respond to memory pressure notifications).

You can mitigate this by controlling the peak memory usage by not loading these downloads into memory. When download large files, it's often better to stream them directly to persistent storage. To do this with AFNetworking, you can set the outputStream of the AFURLConnectionOperation, and it should stream the contents directly to that file, e.g.

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];

BTW, you'll notice that I'm not just calling start on these requests. Personally, I always add them to a queue for which I've specified the maximum number of concurrent operations:

self.downloadQueue = [[NSOperationQueue alloc] init];
self.downloadQueue.maxConcurrentOperationCount = 4;
self.downloadQueue.name = @"com.domain.app.downloadQueue";

I think this is less critical regarding memory usage than the streaming of the results directly to a outputStream using persistent storage, but I find this is another mechanism for managing system resources when initiating many concurrent requests.

查看更多
欢心
3楼-- · 2019-06-14 06:35

You can start using NSURLSession's downloadTask.

I think this will resolve your issue.

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)
     }];
查看更多
登录 后发表回答