Reputation: 1949
I'm downloading multiple files like this
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
HUD = [[MBProgressHUD alloc] initWithWindow:self.view.window];
HUD.labelText = @"Downloading";
HUD.mode = MBProgressHUDModeIndeterminate;
[self.view.window addSubview:HUD];
[HUD show:YES];
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
for (NSString *urlString in URLStrings) {
dispatch_group_async(group, queue, ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
UIImage *image = [[UIImage alloc] initWithData:data];
[library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error) {
}];
});
}
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
HUD.mode = MBProgressHUDModeText;
HUD.labelText = @"Success";
[self performSelector:@selector(hideHUDAndBack) withObject:Nil afterDelay:0.3];
});
});
dispatch_release(group);
And it receives memory warning and shuts down when downloading files with total size of 20MB or more. I tried running it without gcd on the main thread but it still reveived memory warning at the end and shut down. What could be the main cause and how to fix it?
Upvotes: 0
Views: 680
Reputation: 19116
In your approach I don't see any advantage using dispatch lib in order to parallelize network requests. What one can achieve with multiple concurrent network requests is reducing the effect of network latency. However, your simple approach simultaneously introduces memory issues.
In the given scenario, loading videos from a remote server, we can assume that the file size is quite large, and thus latency becomes a minor problem. The dominant factor would be bandwidth. But you cannot load videos faster when you load multiple at once when the bandwidth is the limiting factor.
Thus, I would suggest you try the following even more simple solution:
for (NSString *urlString in URLStrings) {
@autoreleasepool {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
UIImage *image = [[UIImage alloc] initWithData:data];
[library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error) {
// HUD.infoText = @"Saved asset %@, assetURL";
}];
}
}
HUD.mode = MBProgressHUDModeText;
HUD.labelText = @"Download complete";
[self performSelector:@selector(hideHUDAndBack) withObject:Nil afterDelay:0.3];
Note: Since downloading the resources is sequential and synchronous, you should wrap these statements into a block and use dispatch_async
, in order to execute it on a secondary thread (that is, not on the main thread).
What you now should improve is the way you download the videos. Method dataWithContentsOfURL:
is the most inappropriate for loading remote resources. ;)
Upvotes: 2
Reputation: 18816
First, you should monitor memory usage in the Instruments to find out the specific reason.
One thing that could help is including an @autorelease
pool in the block, see for example Using ARC, is it fatal not to have an autorelease pool for every thread?.
But most importantly, good things happen automatically when you use higher-level frameworks, such as NSOperation
class (built on top of GCD) or AFNetworking
library (itself built on top of NSOperation
).
They will create autorelease pools and provide a way to limit number of concurrent downloads, add dependencies, and do other stuff that you won't have to reimplement.
Note also that synchronous methods, like aforementioned dataWithContentsOfURL:
, are likely to be less reasonable about memory footprint, because while they block the thread you are not able to perform any memory management.
Upvotes: 2
Reputation: 5454
By the looks of it, you need to limit the number of concurrent downloads.
Credits should go here: https://stackoverflow.com/a/16105827/727817
Applied to your case it should look something like:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
dispatch_semaphore_t downloadSema = dispatch_semaphore_create(3); // number of concurrent downloads - this should be set depending on the size of your images
for (NSString *urlString in URLStrings) {
dispatch_group_async(group, queue, ^{
dispatch_semaphore_wait(downloadSema, DISPATCH_TIME_FOREVER);
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
UIImage *image = [[UIImage alloc] initWithData:data];
[library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error) {
dispatch_semaphore_signal(downloadSema);
}];
});
}
// ..
dispatch_release(downloadSema);
Upvotes: 1