A sophisticated solution may become quite complex. Downloading and saving 100 URLs is not as easy as 100 times downloading one image.
The most vexing issue that will arise in ambitious but less smart implementations are memory pressures. A third party network library will not automagically solve this problem for you.
One simple - yet viable - approach which tries to avoid memory problems at the cost of a bit performance, is executing the download and save to disk operation all sequentially. This ensures, that only one image will be handled at any time. Thus, you can make safe assumptions about the maximum memory required for this approach.
A solution may look as below:
Suppose, you have an asynchronous method which loads the image data from a given URL:
typedef void (^load_completion_t)(NSData* data, NSError* error);
- (void) loadURL:(NSURL*)url completion:(load_completion_t)completionHandler;
This method will load the whole image data into memory. This isn't really the best way, but IFF we can assume that one image always fits into memory, it becomes a simple solution.
Furthermore, suppose, there is a synchronous method which saves the data to disk:
- (void) saveData:(NSData*)data;
Now, given an array of URLs you can sequentially load and save a number of images as follows:
typedef void(^completion_t)(id result, NSError* error);
- (void) saveImagesWithURLs:(NSMutableArray*)urls
completion:(completion_t)completionHandler
{
if ([urls count] > 0) {
NSURL* url = [urls firstObject];
[urls removeObjectAtIndex:0];
[self loadURL:url completion:^(NSData* imageData, NSError*error){
if (imageData) {
[self saveData:imageData];
[self saveImagesWithURLs:urls completion:completionHandler];
}
else {
// handle error
}
}];
}
else {
// finished
if (completionHandler) {
completionHandler(@"Images saved", nil);
}
}
}
The method above is an "asynchronous loop":
The completion handler of loadURL:completion
will call saveImagesWithURLs:completion:
, much like a recursive invocation. However, this is NOT a recursive method: when the completion handler of method saveImagesWithURLs:completion:
gets executed, saveImagesWithURLs:completion:
already returned.
Given a propert which is an array of URLs:
@property (nonatomic, strong) NSArray* imageURLs;
you can invoke the asynchronous loop as shown below:
[self saveImagesWithURLs:[self.imageURLs mutableCopy]
completion:^(id result, NSError*error){
if (error) {
// handle error
}
else {
// result equals @"Images saved"
}
}];
You can call this method from the main thread. It will NOT block, because it is asynchronous. We also assume, that the completion handlers will be invoked on a private queue, and not on the main thread.