I am using NSURLConnection to make some http requests to my webservice, which returns a large text file. Obviously this blocks the main thread and adds lag, so I want to process this in the background. BTW, I realize that there are third party frameworks out there handle this particular job/situation, but I'm coding this myself because I need to learn about multithreading on iOS.
I can either detach an NSThread or pass the NSURLConnection code to GCD in a block. Both ways work fine initially (to make the http connection and send the request). The problem is how to get the data back from the server. Let me explain.... when my webservice sends the data back, my app is informed via callbacks from the NSURLConnectionDelegate protocol. But by the time this happens, either my detached thread has exited it's target method, or the block has already been processed by GCD an it's off the queue (depending on which of the approaches I have used). Basically, the callback goes unnoticed by my app (unless of course I'm using dispatch_main_queue).
What is the "correct" objective-c way to solve this with each case?
Thanks for any direction
For networking code I would use the asynchronous method on NSULRConnection and process the data I get back on a background queue. Only the data that modifies the UI and needs to be on the main queue would be dispatched (using GCD) to the main queue.
NSOperationQueue *yourQueue = [[NSOperationQueue alloc] init];
[NSULRConnection sendAsynchronousRequest:yourRequest
queue:yourQueue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
// The code inside here gets processed once the url request completes ...
// It happens in the background on "yourQueue".
[self doSomeExpensiveDataProcessingWithData:data];
dispatch_sync(dispatch_get_main_queue(), ^{
// Update UI on the main thread and wait to continue processing the data
[self updateUI];
});
[self doSomeMoreDataProcessing];
dispatch_sync(dispatch_get_main_queue(), ^{
// Update UI on the main thread and wait to continue processing the data
[self finalUpdateOfUI];
});
}];
Outside the example of networking code I generally like asynchronous callbacks as a design pattern. It becomes easy to test different callbacks in isolation and it divides the different callbacks (like error handling and data processing) into different methods which gives more focused code inside those methods.
GCD is very good for quickly doing a few lines of code on another thread or dispatching some work asynchronously.
NSThread is rarely used anymore.
Sounds like a NSRunLoop problem ... Read this for example ...
http://www.sortedbits.com/nsurlconnection-in-its-own-thread/
http://www.cocoaintheshell.com/2011/04/nsurlconnection-synchronous-asynchronous/
... Google for more ...
... why do you want to do it in this way? It's enough to use NSURLConnection asynchronously as it is (on main thread) and then dispatch your heavy processing of these data when they're received. In other words, NSURLConnection delegate methods will be called on the main thread, but then dispatch data processing only to not block main thread.
Related
How do I prevent from a dispatch_group from getting stuck? I have found to be possible to get stuck in the following code (with or without the dispatch_group_wait call) if one of the images I attempt to load is not loaded (e.g. due to bad url). The block in dispatch_group_notify is never called.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();
for (...) {
if (...) {
dispatch_group_enter(group);
dispatch_async(queue, ^{
[self loadImageWithUrl:url onCompletion:^{
dispatch_group_leave(group);
}];
});
}
}
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)));
dispatch_group_notify(group, queue, ^{
NSLog(#"load image complete");
});
dispatch_group_notify queues its block when the group is complete. Your group never completes. So don't use dispatch_group_notify. Just use dispatch_group_wait as you are to wait with a timeout, then dispatch your block:
...
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)));
dispatch_async(queue, ^{
NSLog(#"load image complete");
});
If you want to mimic a dispatch_group_notify with a timeout, just do the above in its own async block:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)));
dispatch_sync(queue, ^{
NSLog(#"load image complete");
});
});
Note that you can use the return value of dispatch_group_wait to determine if everything completed or if it timed-out if that is useful information to you.
Keep in mind that the previous blocks will not be cancelled, so they may eventually run their completion blocks. You may need to add cancellation logic to the system if that's a problem.
I do not think the issue is with your group notify process. For me, the issue that leaps out at me is that, rather than trying to handle the scenario where the completion block is not called, that you change loadImageWithUrl to ensure that it always calls the completion block, whether successful or not. You might even want to add a NSError parameter to the block or something like that, so the caller will be notified if something failed (for example to warn the user, or initiate Reachability process that will wait for the connection to be re-established before attempting a retry, etc).
So, it might look like:
- (void)loadImageWithUrl:(NSURL *)url loadImageWithUrl:(void (^)(NSError *error))block
{
BOOL success;
NSError *error;
// do your download, setting `success` and `error` appropriately
// then, when done, call the completion block, whether successful or not
if (block) {
if (success) {
block(nil);
} else {
block(error);
}
}
}
Clearly, the details of the above are entirely dependent upon how you're doing these requests, but that's the basic idea. Then, you just make sure that your caller is changed to include this extra parameter:
for (...) {
if (...) {
dispatch_group_enter(group);
dispatch_async(queue, ^{
[self loadImageWithUrl:url onCompletion:^(NSError *error){
if (error) {
// handle the error however you want, if you want
}
dispatch_group_leave(group);
}];
});
}
}
I care less about how you choose to handle the error than I do in encouraging you ensure your completion block is called regardless of whether the download was successful or not. This ensures that the number of times you enter the group is perfectly balanced with the number of times you leave the group.
Having said that, when downloading many resources, GCD is ill-suited for this task. The issue is that it's non-trivial to constrain GCD to how many concurrent tasks can be performed at one time. Generally, you want to constrain how many requests that can run concurrently. You do this because (a) there's a limit as to how many NSURLSessionTask or NSURLConnection requests can run concurrently anyway; (b) if you run more than that, on slow connections you run serious risk of requests timing-out unnecessarily; (c) you can reduce your app's peak memory usage; but (d) you still enjoy concurrency, striking a balance between memory usage and optimal network bandwidth optimization.
To accomplish this, a common solution is to use operation queues rather than GCD's dispatch queues. You can then wrap your download requests in NSOperation objects and add these network operation to a NSOperationQueue for which you have set some reasonable maxConcurrentOperationCount (e.g. 4 or 5). And instead of a dispatch group notify, you can add a completion operation which is dependent upon the other operations you've added to your queue.
If you don't want to implement this yourself, you can use AFNetworking or SDWebImage, which can facilitate the downloading of images using operation queues to manage the download process.
And one final thought is that many apps adopt a lazy loading process, where images are seamlessly loaded as they're needed. It avoids consuming too much of the user's data plan performing some bulk download (or risking that the image the user needs first is backlogged behind a bunch of other images they don't immediately need). Both AFNetworking and SDWebImage offer UIImageView categories that offer an incredibly simple lazy loading of images.
Would it be possible to do a synchronous load of the image in the inner blocks? That way you could use dispatch_group_async() instead of the manually keeping track of the enter/leave paradigm.
I suspect the error lies in how the blocks complete and how the context is not that correct, it seems weird to me that you enter a group from outside of the block/context you leave the group from.
Finally, are you sure the completion block of the image loading is always called? Is it possible that when the request fails the completion is not called and thus the group counter is never decremented?
Sorry about my initial answer btw, I misread the question totally.
EDIT: Now that I think about what the goal is (synchronising after all images have loaded), it seems that the approach is not really reasonable. Does the code need to block until all the images are loaded? If not, then assuming all completion blocks are fired on a single thread, I would simply keep track of the number of blocks that have been fired and decrement that count in the completion block. When the last one completes, then the contents of the current dispatch_group_notify() could be executed.
Another, perhaps a bit more futureproof option would be to refactor the image loading code to either offer a synchronous way of fetching an image (meant to be used in cases like this) or offer an async API that is capable taking a dispatch group/queue, this obviously assumes that the internals of the image loader uses GCD.
Finally, you could write a NSOperation subclass, that takes care of a single image loading procedure, then those operations could be used in an NSOperationQueue (offering a bit more abstraction from GCD) that can be easily used to keep track how many operations are ongoing and when they all finish.
The problem is your use of dispatch_group_async(). It should not be used unless you are doing tasks that are synchronous that you want to be done asynchronously. Your loadImageWithUrl() is already asynchronous. This is how you should structure your use of dispatch_group.
dispatch_group_t group = dispatch_group_create();
for (...) {
if (...) {
dispatch_group_enter(group);
[self loadImageWithUrl:url onCompletion:^{
dispatch_group_leave(group);
}];
}
}
dispatch_group_notify(group, queue, ^{
NSLog(#"load image complete");
});
Also dispatch_group_wait is the alternative to using dispatch_group_notify. It should only be used if you want to wait synchronously for the group to finish.
I have an iPad app that runs a regular server sync process - it runs every 10 seconds or so. The sync process downloads records that are inserted into a CoreData SQL-based store. On occasion the number of records being handled can run into the hundreds or thousands.
The current sync process is based upon an Asynchronous NSURLConnection triggered by the main thread. Once all NSData has been gathered by the async call then the main thread fires an NSOperation in the background to parse the NSData and insert it into the db.
So, the NSURLConnection is running asynchronously, and the db insert is running in a background NSOperation. However, the orchestration of the NSURLConnection and the NSOperation is done in the main thread. Given that there are large quantities of data being downloaded then I'm thinking that even this small amount of orchestration on the main thread may be impacting my UI responsiveness.
So, I'm thinking of refactoring the code into a single background NSOperation and making the NSURLConnection into a synchronous call. A single NSOperation would then synchronously download the NSData and manage the db inserts.
Before I embark on quite a major refactoring I would be interested in people's views on whether this is a good decision.
With the current mechanism I notice some occasional hesitation in the UI. By placing the entire mechanism in a background NSOperation I am hoping that the UI will remain responsive.
Any words of wisdom would be very much appreciated.
Thanks.
I wouldn't recommend switching to a synchronous connection. Asynchronous connection uses less memory and gives you more control over the download process. To avoid UI freeze, I would try to force background processing only inside NSURLConnection delegate methods, like so:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self.mutableData appendData:data];
});
}
EDIT:
As #jrturton stated in comments, you can specify the queue for NSURLConnection delegate methods. Use setDelegateQueue: method (iOS 5.0+).
I am writing an application with a plugin system. Plugins must work on the main thread (this is not part of the question, I'm not looking for a bunch of answers that say I should remove this requirement).
Plugins get initialised asynchronously so the UI doesn't hang for a few seconds on launch, but other code immediately starts to interact with the plugins after launch. This obviously needs to be delayed until the plugins have finished loading.
Here is what I've got so far...
// Create operation queue
dispatch_queue_t queue = dispatch_queue_create(...);
dispatch_suspend(queue);
// Load the plugins
dispatch_group_t group = dispatch_group_create();
for each plugin {
dispatch_group_async(group, dispatch_get_main_queue(), ^{
load...
});
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
dispatch_resume(queue);
});
// Add operations that interact with the plugins
dispatch_async(queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
operation...
});
});
This will mean that any operations submitted won't start until the plugins have finished loading, however, any new operations will go through two queues before actually being processed. Is this a large overhead? Would it be worth queueing to begin with, and then swapping out method implementations when ready for one that doesn't bother queueing? This would be more tricky to do and I don't know if it would be worth it.
Finally, is there a better design pattern for this type of problem? Perhaps I should be using NSOperations and NSOperationQueues with dependencies? Or would they have a higher overhead than basic GCD operations?
The "double overhead" is actually really low, but there is a slightly better design pattern you can use for this that is also more intuitive. Create your operation queue and then use dispatch_set_target_queue(queue, dispatch_get_main_queue()) to make it, in essence, a sub-queue of the main queue. This will ensure it executes on the main thread while not requiring you to do the cross-submission - you'll just submit the plug-in operation(s) directly to the operation queue.
Are Objective-C blocks always executed in a separate thread?
Specifically, I'm asking about the sendAsynchronousRequest:queue:completionHandler method of the NSURLConnection class. This is the scenario:
Main thread (1st thread) calls the sendAsynchronousRequest method the
sendAsynchronousRequest is executed on a 2nd thread, managed by the
NSOperationQueue when method is completed and calls
commpletionHandler, which thread is it executed on? 2nd thread? yet
another 3rd thread? or the 1st thread?
Thanks!
It executes it on whatever operation queue you specify as the queue argument:
Loads the data for a URL request and executes a handler block on an operation queue when the request completes or fails.
The queue parameter is documented as:
The operation queue to which the handler block is dispatched when the request completes or failed.
So it's really up to the NSOperationQueue exactly how many threads are used. I'd expect pooling behaviour - so while there can be multiple threads, I wouldn't expect a different thread per handler, necessarily.
A block is just a closure, like you have them in python or functional languages. They don't "run on a thread" they run where they are called.
int main(void)
{
void (^f)(void) { printf("hello world!\n"); }
f();
return 0;
}
Does just what you think it does, no dispatch queues, no threads, no nothing.
Though, once you have blocks with all their nice capture semantics, it's very tempting to have APIs to schedule their execution everywhere. But basically, a block, is just the same as a function pointer and an ad-hoc struct containing all the variable captured, passed as an argument to the callback (it's even how it's implemented in the compiler).
As others have said, it will run on whatever queue you have specified. If this is a background queue, and you want to execute some code on the main thread, you can iclude a GCD block accessing the main queue. Your completion block would look something like this:
^(NSURLResponse *response, NSData *data, NSError*error){
// do whatever in the background
dispatch_async(dispatch_get_main_queue(), ^{
// this block will run on the main thread
});
}
Blocks are executed wherever they are told. Wrapping code in a block does not affect the thread or queue it will be run on. In your particular case, as documented, the completion block is executed on the queue that is passed in in the queue parameter.
I'm not sure, for your purposes, if you really need to distinguish between a queue and a thread, the key principle is that the URL request is performed asynchronously to the calling code, and the completion block is performed on the queue passed in as the method parameter.
I need to implement posting some data to a web server in the background. Just to clarify, by "in the background", I don't mean the normal way of showing a spinning icon and posting data to a web service using something like an AsyncTask or ASIHTTPRequest's [request startAsynchronous] method. I need to maintain a queue of data that a Thread can asychronously start processing and posting to a Web service while the user is working in the application.
I'm looking for some help on designing a queue like that, especially in some edge cases like User receiving a call, logging out of the application while the the post is happening, user leaving the application to goto a different one while a post is happening and the like. How would you handle these cases? Is there any source code you can recommend that does this?
Thanks,
Teja.
I've started using NSOperationQueue in my own work lately, for controlling background network requests. NSOperation deals with most of the boilerplate code necessary for asynchronously running tasks (such as network operations) on threads in the background (or foreground, if necessary for UI updates).
It also allows dependencies across queues; for example, I use two queues in my application:
The first schedules image downloads, at a max concurrency of 2 at a time, in the background. Each image download has a corresponding completion handler (as an NSBlockOperation) that is dependent on the image download completing. These operations sit on the [NSOperationQueue mainQueue], which operates on the main thread, allowing them to update UI (specifically, the corresponding UIImageView).
Note that NSOperation and NSOperationQueue are not for network requests only, but any operation that can be divided into atomic tasks and scheduled concurrently.
Here are Apple's intro docs on the topic.
Having implemented something similar myself, I would recommend using a service and not a thread to do network calls. That way even if your activity gets killed you're sure your network calls will be executed.
Then to implement the queue i suggest you take a look into IntentService (http://developer.android.com/reference/android/app/IntentService.html)
from the docs:
IntentService is a base class for Services that handle asynchronous
requests (expressed as Intents) on demand. Clients send requests
through startService(Intent) calls; the service is started as needed,
handles each Intent in turn using a worker thread, and stops itself
when it runs out of work.
This "work queue processor" pattern is commonly used to offload tasks
from an application's main thread. The IntentService class exists to
simplify this pattern and take care of the mechanics. To use it,
extend IntentService and implement onHandleIntent(Intent).
IntentService will receive the Intents, launch a worker thread, and
stop the service as appropriate.
All requests are handled on a single worker thread -- they may take as
long as necessary (and will not block the application's main loop),
but only one request will be processed at a time.
If your application is simple enough you can use sendBroadCast() to share info and notifications between your activity and the IntentService
Create a singleton that encapsulate a thread :
In the initialisation of your object :
[NSThread detachNewThreadSelector:#selector(mainPosterThread) toTarget:self withObject:nil];
- (void)mainDownloaderThread
{
if( [NSThread respondsToSelector:#selector(setThreadPriority:)] )
{
[NSThread setThreadPriority:0.1];
}
NSString *urlToDownload = nil;
while(shouldRun)
{
// Take next data to post in a queue (lastObject of a NSArray for example)
if( nextDataToPost )
{
// Post
}
else
{
// Sleep for some time.
}
}
}
You can also have methods for stopping / starting the thread while the app go background / foreground on a multitask supported device. If no multitasking supported, save the post data in the queue at stop time (if not too long) and restore them at start. The biggest chalenge is to manage be able to cancel the current upload while app will ends.
This is an issue which I've been perfecting in every new application I write. Basically I wanted network functionality which is asynchronous and which was written by me using native functionality. I'd be happy to show you some of this code if you're interested.
First of all, I suggest that you make all network calls on the main thread asynchronously rather than synchronously, using a delegate. This way serialization/synchronization/concurrency is not an issue. And since classes are network delegates, I'd just set up one class to where a new connection has a new delegate instance.
[[NSURLConnection alloc] initWithRequest:request delegate:del] autorelease];
e.g.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data