I'm trying to build a bulk image downloader, where images can be added to a queue on the fly to be downloaded, and I can find out the progress and when they're done downloading.
Through my reading it seems like NSOperationQueue
for the queue functionality and NSURLSession
for the network functionality seems like my best bet, but I'm confused as to how to use the two in tandem.
I know I add instances of NSOperation
to the NSOperationQueue
and they get queued. And it seems I create a download task with NSURLSessionDownloadTask
, and multiple if I need multiple tasks, but I'm not sure how I put the two together.
NSURLSessionDownloadTaskDelegate
seems to have all the information I need for download progress and completion notifications, but I also need to be able to stop a specific download, stop all the downloads, and deal with the data I get back from the download.
GCD is a low-level C-based API that enables very simple use of a task-based concurrency model. NSOperation and NSOperationQueue are Objective-C classes that do a similar thing. NSOperation was introduced first, but as of 10.5 and iOS 2, NSOperationQueue and friends are internally implemented using GCD .
A queue that regulates the execution of operations.
Your intuition here is correct. If issuing many requests, having an NSOperationQueue
with maxConcurrentOperationCount
of 4 or 5 can be very useful. In the absence of that, if you issue many requests (say, 50 large images), you can suffer timeout problems when working on a slow network connection (e.g. some cellular connections). Operation queues have other advantages, too (e.g. dependencies, assigning priorities, etc.), but controlling the degree of concurrency is the key benefit, IMHO.
If you are using completionHandler
based requests, implementing operation-based solution is pretty trivial (it's the typical concurrent NSOperation
subclass implementation; see the Configuring Operations for Concurrent Execution section of the Operation Queues chapter of the Concurrency Programming Guide for more information).
If you are using the delegate
based implementation, things start to get pretty hairy pretty quickly, though. This is because of an understandable (but incredibly annoying) feature of NSURLSession
whereby the task-level delegates are implemented at the session-level. (Think about that: Two different requests that require different handling are calling the same delegate method on the shared session object. Egad!)
Wrapping a delegate-based NSURLSessionTask
in an operation can be done (I, and others I'm sure, have done it), but it involves an unwieldy process of having the session object maintain a dictionary cross referencing task identifiers with task operation objects, have it pass these task delegate methods passed to the task object, and then have the task objects conform to the various NSURLSessionTask
delegate protocols. It's a pretty significant amount of work required because NSURLSession
doesn't provide a maxConcurrentOperationCount
-style feature on the session (to say nothing of other NSOperationQueue
goodness, like dependencies, completion blocks, etc.).
And it's worth pointing out that operation-based implementation is a bit of a non-starter with background sessions, though. Your upload/download tasks will continue to operate well after the app has been terminated (which is a good thing, that's fairly essential behavior in a background request), but when your app is restarted, the operation queue and all of its operations are gone. So you have to use a pure delegate-based NSURLSession
implementation for background sessions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With