Alternatives to dispatch_get_current_queue() for completion blocks in iOS 6? - objective-c

I have a method that accepts a block and a completion block. The first block should run in the background, while the completion block should run in whatever queue the method was called.
For the latter I always used dispatch_get_current_queue(), but it seems like it's deprecated in iOS 6 or higher. What should I use instead?

The pattern of "run on whatever queue the caller was on" is appealing, but ultimately not a great idea. That queue could be a low priority queue, the main queue, or some other queue with odd properties.
My favorite approach to this is to say "the completion block runs on an implementation defined queue with these properties: x, y, z", and let the block dispatch to a particular queue if the caller wants more control than that. A typical set of properties to specify would be something like "serial, non-reentrant, and async with respect to any other application-visible queue".
** EDIT **
Catfish_Man put an example in the comments below, I'm just adding it to his answer.
- (void) aMethodWithCompletionBlock:(dispatch_block_t)completionHandler
{
dispatch_async(self.workQueue, ^{
[self doSomeWork];
dispatch_async(self.callbackQueue, completionHandler);
}
}

This is fundamentally the wrong approach for the API you are describing to take. If an API accepts a block and a completion block to run, the following facts need to be true:
The "block to run" should be run on an internal queue, e.g. a queue which is private to the API and hence entirely under that API's control. The only exception to this is if the API specifically declares that the block will be run on the main queue or one of the global concurrent queues.
The completion block should always be expressed as a tuple (queue, block) unless the same assumptions as for #1 hold true, e.g. the completion block will be run on a known global queue. The completion block should furthermore be dispatched async on the passed-in queue.
These are not just stylistic points, they're entirely necessary if your API is to be safe from deadlocks or other edge-case behavior that WILL otherwise hang you from the nearest tree someday. :-)

The other answers are great, but for the me the answer is structural. I have a method like this that's on a Singleton:
- (void) dispatchOnHighPriorityNonMainQueue:(simplest_block)block forceAsync:(BOOL)forceAsync {
if (forceAsync || [NSThread isMainThread])
dispatch_async_on_high_priority_queue(block);
else
block();
}
which has two dependencies, which are:
static void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}
and
typedef void (^simplest_block)(void); // also could use dispatch_block_t
That way I centralize my calls to dispatch on the other thread.

You should be careful about your use of dispatch_get_current_queue in the first place. From the header file:
Recommended for debugging and logging purposes only:
The code
must not make any assumptions about the queue returned, unless it
is one of the global queues or a queue the code has itself created.
The code must not assume that synchronous execution onto a queue is
safe from deadlock if that queue is not the one returned by
dispatch_get_current_queue().
You could do either one of two things:
Keep a reference to the queue you originally posted on (if you created it via dispatch_queue_create), and use that from then on.
Use system defined queues via dispatch_get_global_queue, and keep a track of which one you're using.
Effectively whilst previously relying on the system to keep track of the queue you are on, you are going to have to do it yourself.

Apple had deprecated dispatch_get_current_queue(), but left a hole in another place, so we still able to get current dispatch queue:
if let currentDispatch = OperationQueue.current?.underlyingQueue {
print(currentDispatch)
// Do stuff
}
This works for main queue at least.
Note, that underlyingQueue property is available since iOS 8.
If you need to perform the completion block in the original queue, you also may use OperationQueue directly, I mean without GCD.

For those who still need in queue comparing, you could compare queues by their label or specifies.
Check this https://stackoverflow.com/a/23220741/1531141

This is a me too answer. So I will talk about our use case.
We have a services layer and the UI layer (among other layers). The services layer runs tasks in the background. (Data manipulation tasks, CoreData tasks, Network calls etc). The service layer has a couple operation queues to satisfy the needs of the UI layer.
The UI layer relies on the services layer to do its work and then run a success completion block. This block can have UIKit code in it. A simple use case is to get all messages from the server and reload the collection view.
Here we guarantee that the blocks that are passed into the services layer are dispatched on the queue on which the service was invoked on. Since dispatch_get_current_queue is a deprecated method, we use the NSOperationQueue.currentQueue to get the caller's current queue. Important note on this property.
Calling this method from outside the context of a running operation
typically results in nil being returned.
Since we always invoke our services on a known queue (Our custom queues and Main queue) this works well for us. We do have cases where serviceA can call serviceB which can call serviceC. Since we control where the first service call is being made from, we know the rest of the services will follow the same rules.
So NSOperationQueue.currentQueue will always return one of our Queues or the MainQueue.

Related

NSPrivateQueueConcurrencyType serial or concurrent?

As the title says, the question is, if a NSManagedObjectContext with concurrency type NSPrivateQueueConcurrencyType is serial or concurrent.
More specifically, if I call
[managedObjectContext performBlock:^{
}];
with a long running task, will other calls to that context with performBlock be blocked until the first one finished?
It is serial queue from Apple docs.
Or you can simply try to run this code and see the result. The numbers will be printed serially.
let privateMOC = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateMOC.perform {
for i in 0...8000 {
if i.isMultiple(of: 3000) {
print("1")
}
}
}
privateMOC.perform {
for i in 0...8000 {
if i.isMultiple(of: 3000) {
print("2")
}
}
}
I don't believe it's documented either way. However, Core Data is generally not thread-safe, and the performBlock and performBlockAndWait methods are ways of dealing with that-- by putting all of your Core Data work on a single queue. As such I'd be extremely surprised if it was a concurrent queue, since the whole point is to avoid concurrency.
I cannot find any evidence in the official docs but recently I worked with NSPrivateQueueConcurrencyType and I remember it was serial. Also this blog post states that:
When an NSManagedObjectContext is created with the initializer initWithConcurrencyType:NSPrivateQueueConcurrencyType] or -[initWithConcurrencyType:NSMainQueueConcurrencyType] all access to the context and it’s managed objects must go through the method -[performBlock: or -[performBlockAndWait:] . Core Data is using a serial queue to ensure that operations on the context are performed in order, and that only one operation is happening at a time
It's a serial queue. You code will be performed serially when you call performBlock and performBlockAndWait if you specify NSPrivateQueueConcurrencyType.
Apple doesn't pick the word PrivateQueue randomly, Private Queue = Serial Queue in Apple's documents. See here for the Serial Queue description
Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue.
Also, I have double checked that in debugging. Please see the last thread in the below screenshot:
NSMainQueueConcurrencyType uses the main queue. The main queue is bound to the main thread and therefore serial.
The main dispatch queue is a globally available serial queue that executes tasks on the application’s main thread.
I have tested that all tasks will be executed in sequence, but It may be executed in a different thread
backgroundContext.perform {
print("perform------:\(self.number)", Thread.current)
self.number += 1
}

Are Objective-C blocks always executed in a separate thread?

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.

Difference between Main Queue / Current Queue & Main Thread / Background Thread in this case?

I'm executing the following method :
MotionHandler.m
-(void)startAccelerationUpdates
{
[motionManagerstartDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]withHandler:^(CMDeviceMotion *motion, NSError *error){.....}
}
on a background thread, as follows:
[currentMotionHandler performSelectorInBackground:#selector(startAccelerationUpdates) withObject:nil];
But the above method uses the main Queue (which is on the main thread) to perform the necessary updates even though I'm calling it on a background thread.. So are acceleration updates being performed on a background thread or on the main thread, I'm confused..?
What's even more interesting is that when I call the above method on background thread again, but this time using the current Queue, I get no updates. Could someone please explain the difference between running something on :
1. a background thread but on the main queue
2. a background thread but on the current queue
3. the main thread but on the main queue
4. the main thread but on the current queue
in the current implementation? Thank you!
I'll give it a shot. First, without being told by the NSOperationQueue class reference, we could not infer anything about what thread the 'mainQueue' would run on. Reading it we see that in fact that queue runs its operations on the mainThread, the one the UI uses, so you can update the UI in operations posted to that queue. Although it doesn't say it, these operations must be serial, due to them being executed by the runLoop (its possible they can get preempted too, not 100% sure of that).
The purpose for currentQueue is so that running operations can determine the queue they are on, and so they can potentially queue new operations on that queue.
a background thread but on the main queue
Not possible - the NSOperation's mainQueue is always associated with the mainThread.
a background thread but on the current queue
When you create a NSOperationQueue, and add NSOperations to it, those get run on background threads managed by the queue. Any given operation can query what thread its on, and that thread won't change while it runs. That said, a second operation on that queue may get run on a different thread.
the main thread but on the main queue
See 1)
the main thread but on the current queue
If you queue an operation to the mainQueue (which we know is always on the mainThread), and you ask for the currentQueue, it will return the mainQueue:
[NSOperationQueue currentQueue] == [NSOperationQueue mainQueue];
You are confusing queues with threads. Especially since NSOpertionQueue has been rewritten to use GCD, there is little connection between queues and specific threads (except for the special case of the main thread).
Operations/blocks/tasks - whatever you want to call them - are inserted into a queue, and "worker thread(s)" pull these off and perform them. You have little control over which exact thread is going to do the work -- except for the main queue. Note, this is not exactly right, because it's a simplification, but it's true enough unless you are doing something quite advanced and specific.
So, none of your 4 scenarios even make sense, because you can't, for example, run something on "a background thread but on the main queue."
Now, your method startAccelerationUpdates specifically tells the CMMotionManager to put your handler on the main queue. Thus, when startAccelerationUpdates is called, it gets run in whichever thread it's running, but it schedules the handler to be executed on the main thread.
To somewhat complicate things, you are calling the startAccelerationUpdates method by calling performSelectorInBackground. Again, you don't know which thread is going to actually invoke startAccelerationUpdates, but it will not be the main thread.
However, in your case, all that thread is doing is calling startAccelerationUpdates which is starting motion updates, and telling them to be handled on the main thread (via the main queue).
Now, here's something to dissuade you from using the main queue to handle motion events, directly from the documentation...
Because the processed events might arrive at a high rate, using the main operation queue is not recommended.
Unfortunately, your statement
What's even more interesting is that when I call the above method on
background thread again, but this time using the current Queue, I get
no updates.
does not provide enough information to determine what you tried, how you tried it, or why you think it did not work. So, I'll make a guess... which may be wrong.
I'll key on your use of the current Queue.
I assume you mean that you substitute [NSOperationQueue mainQueue] with [NSOperationQueue currentQueue].
Well, let's see what that does. Instead of using the main queue, you will be using "some other" queue. Which one? Well, let's look at the documentation:
currentQueue
Returns the operation queue that launched the current
operation.
+ (id)currentQueue
Return Value
The operation queue that started the operation or nil if the queue could not be determined.
Discussion
You can use this method from within a running operation
object to get a reference to the operation queue that started it.
Calling this method from outside the context of a running operation
typically results in nil being returned.
Please note the discussion section. If you call this when you are not running an operation that was invoked from an NSOperationQueue, you will get nil which means there will be no queue on which to place your handler. So, you will get nothing.
You must specify which queue is to be used, if you want to use an NSOperationQueue other than the main queue. So, if that's the route you want to go, just create your own operation queue to handle motion events, and be off!
Good Luck!

Why can't we use a dispatch_sync on the current queue?

I ran into a scenario where I had a delegate callback which could occur on either the main thread or another thread, and I wouldn't know which until runtime (using StoreKit.framework).
I also had UI code that I needed to update in that callback which needed to happen before the function executed, so my initial thought was to have a function like this:
-(void) someDelegateCallback:(id) sender
{
dispatch_sync(dispatch_get_main_queue(), ^{
// ui update code here
});
// code here that depends upon the UI getting updated
}
That works great, when it is executed on the background thread. However, when executed on the main thread, the program comes to a deadlock.
That alone seems interesting to me, if I read the docs for dispatch_sync right, then I would expect it to just execute the block outright, not worrying about scheduling it into the runloop, as said here:
As an optimization, this function invokes the block on the current thread when possible.
But, that's not too big of a deal, it simply means a bit more typing, which lead me to this approach:
-(void) someDelegateCallBack:(id) sender
{
dispatch_block_t onMain = ^{
// update UI code here
};
if (dispatch_get_current_queue() == dispatch_get_main_queue())
onMain();
else
dispatch_sync(dispatch_get_main_queue(), onMain);
}
However, this seems a bit backwards. Was this a bug in the making of GCD, or is there something that I am missing in the docs?
dispatch_sync does two things:
queue a block
blocks the current thread until the block has finished running
Given that the main thread is a serial queue (which means it uses only one thread), if you run the following statement on the main queue:
dispatch_sync(dispatch_get_main_queue(), ^(){/*...*/});
the following events will happen:
dispatch_sync queues the block in the main queue.
dispatch_sync blocks the thread of the main queue until the block finishes executing.
dispatch_sync waits forever because the thread where the block is supposed to run is blocked.
The key to understanding this issue is that dispatch_sync does not execute blocks, it only queues them. Execution will happen on a future iteration of the run loop.
The following approach:
if (queueA == dispatch_get_current_queue()){
block();
} else {
dispatch_sync(queueA, block);
}
is perfectly fine, but be aware that it won't protect you from complex scenarios involving a hierarchy of queues. In such case, the current queue may be different than a previously blocked queue where you are trying to send your block. Example:
dispatch_sync(queueA, ^{
dispatch_sync(queueB, ^{
// dispatch_get_current_queue() is B, but A is blocked,
// so a dispatch_sync(A,b) will deadlock.
dispatch_sync(queueA, ^{
// some task
});
});
});
For complex cases, read/write key-value data in the dispatch queue:
dispatch_queue_t workerQ = dispatch_queue_create("com.meh.sometask", NULL);
dispatch_queue_t funnelQ = dispatch_queue_create("com.meh.funnel", NULL);
dispatch_set_target_queue(workerQ,funnelQ);
static int kKey;
// saves string "funnel" in funnelQ
CFStringRef tag = CFSTR("funnel");
dispatch_queue_set_specific(funnelQ,
&kKey,
(void*)tag,
(dispatch_function_t)CFRelease);
dispatch_sync(workerQ, ^{
// is funnelQ in the hierarchy of workerQ?
CFStringRef tag = dispatch_get_specific(&kKey);
if (tag){
dispatch_sync(funnelQ, ^{
// some task
});
} else {
// some task
}
});
Explanation:
I create a workerQ queue that points to a funnelQ queue. In real code this is useful if you have several “worker” queues and you want to resume/suspend all at once (which is achieved by resuming/updating their target funnelQ queue).
I may funnel my worker queues at any point in time, so to know if they are funneled or not, I tag funnelQ with the word "funnel".
Down the road I dispatch_sync something to workerQ, and for whatever reason I want to dispatch_sync to funnelQ, but avoiding a dispatch_sync to the current queue, so I check for the tag and act accordingly. Because the get walks up the hierarchy, the value won't be found in workerQ but it will be found in funnelQ. This is a way of finding out if any queue in the hierarchy is the one where we stored the value. And therefore, to prevent a dispatch_sync to the current queue.
If you are wondering about the functions that read/write context data, there are three:
dispatch_queue_set_specific: Write to a queue.
dispatch_queue_get_specific: Read from a queue.
dispatch_get_specific: Convenience function to read from the current queue.
The key is compared by pointer, and never dereferenced. The last parameter in the setter is a destructor to release the key.
If you are wondering about “pointing one queue to another”, it means exactly that. For example, I can point a queue A to the main queue, and it will cause all blocks in the queue A to run in the main queue (usually this is done for UI updates).
I found this in the documentation (last chapter):
Do not call the dispatch_sync function from a task that is executing
on the same queue that you pass to your function call. Doing so will
deadlock the queue. If you need to dispatch to the current queue, do
so asynchronously using the dispatch_async function.
Also, I followed the link that you provided and in the description of dispatch_sync I read this:
Calling this function and targeting the current queue results in deadlock.
So I don't think it's a problem with GCD, I think the only sensible approach is the one you invented after discovering the problem.
I know where your confusion comes from:
As an optimization, this function invokes the block on the current
thread when possible.
Careful, it says current thread.
Thread != Queue
A queue doesn't own a thread and a thread is not bound to a queue. There are threads and there are queues. Whenever a queue wants to run a block, it needs a thread but that won't always be the same thread. It just needs any thread for it (this may be a different one each time) and when it's done running blocks (for the moment), the same thread can now be used by a different queue.
The optimization this sentence talks about is about threads, not about queues. E.g. consider you have two serial queues, QueueA and QueueB and now you do the following:
dispatch_async(QueueA, ^{
someFunctionA(...);
dispatch_sync(QueueB, ^{
someFunctionB(...);
});
});
When QueueA runs the block, it will temporarily own a thread, any thread. someFunctionA(...) will execute on that thread. Now while doing the synchronous dispatch, QueueA cannot do anything else, it has to wait for the dispatch to finish. QueueB on the other hand, will also need a thread to run its block and execute someFunctionB(...). So either QueueA temporarily suspends its thread and QueueB uses some other thread to run the block or QueueA hands its thread over to QueueB (after all it won't need it anyway until the synchronous dispatch has finished) and QueueB directly uses the current thread of QueueA.
Needless to say that the last option is much faster as no thread switch is required. And this is the optimization the sentence talks about. So a dispatch_sync() to a different queue may not always cause a thread switch (different queue, maybe same thread).
But a dispatch_sync() still cannot happen to the same queue (same thread, yes, same queue, no). That's because a queue will execute block after block and when it currently executes a block, it won't execute another one until the currently executed is done. So it executes BlockA and BlockA does a dispatch_sync() of BlockB on the same queue. The queue won't run BlockB as long as it still runs BlockA, but running BlockA won't continue until BlockB has ran. See the problem? It's a classical deadlock.
The documentation clearly states that passing the current queue will cause a deadlock.
Now they don’t say why they designed things that way (except that it would actually take extra code to make it work), but I suspect the reason for doing things this way is because in this special case, blocks would be “jumping” the queue, i.e. in normal cases your block ends up running after all the other blocks on the queue have run but in this case it would run before.
This problem arises when you are trying to use GCD as a mutual exclusion mechanism, and this particular case is equivalent to using a recursive mutex. I don’t want to get into the argument about whether it’s better to use GCD or a traditional mutual exclusion API such as pthreads mutexes, or even whether it’s a good idea to use recursive mutexes; I’ll let others argue about that, but there is certainly a demand for this, particularly when it’s the main queue that you’re dealing with.
Personally, I think that dispatch_sync would be more useful if it supported this or if there was another function that provided the alternate behaviour. I would urge others that think so to file a bug report with Apple (as I have done, ID: 12668073).
You can write your own function to do the same, but it’s a bit of a hack:
// Like dispatch_sync but works on current queue
static inline void dispatch_synchronized (dispatch_queue_t queue,
dispatch_block_t block)
{
dispatch_queue_set_specific (queue, queue, (void *)1, NULL);
if (dispatch_get_specific (queue))
block ();
else
dispatch_sync (queue, block);
}
N.B. Previously, I had an example that used dispatch_get_current_queue() but that has now been deprecated.
Both dispatch_async and dispatch_sync perform push their action onto the desired queue. The action does not happen immediately; it happens on some future iteration of the run loop of the queue. The difference between dispatch_async and dispatch_sync is that dispatch_sync blocks the current queue until the action finishes.
Think about what happens when you execute something asynchronously on the current queue. Again, it does not happen immediately; it puts it in a FIFO queue, and it has to wait until after the current iteration of the run loop is done (and possibly also wait for other actions that were in the queue before you put this new action on).
Now you might ask, when performing an action on the current queue asynchronously, why not always just call the function directly, instead of wait until some future time. The answer is that there is a big difference between the two. A lot of times, you need to perform an action, but it needs to be performed after whatever side effects are performed by functions up the stack in the current iteration of the run loop; or you need to perform your action after some animation action that is already scheduled on the run loop, etc. That's why a lot of times you will see the code [obj performSelector:selector withObject:foo afterDelay:0] (yes, it's different from [obj performSelector:selector withObject:foo]).
As we said before, dispatch_sync is the same as dispatch_async, except that it blocks until the action is completed. So it's obvious why it would deadlock -- the block cannot execute until at least after the current iteration of the run loop is finished; but we are waiting for it to finish before continuing.
In theory it would be possible to make a special case for dispatch_sync for when it is the current thread, to execute it immediately. (Such a special case exists for performSelector:onThread:withObject:waitUntilDone:, when the thread is the current thread and waitUntilDone: is YES, it executes it immediately.) However, I guess Apple decided that it was better to have consistent behavior here regardless of queue.
Found from the following documentation.
https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/index.html#//apple_ref/c/func/dispatch_sync
Unlike dispatch_async, "dispatch_sync" function does not return until the block has finished. Calling this function and targeting the current queue results in deadlock.
Unlike with dispatch_async, no retain is performed on the target queue. Because calls to this function are synchronous, it "borrows" the reference of the caller. Moreover, no Block_copy is performed on the block.
As an optimization, this function invokes the block on the current thread when possible.

Background network calls - iOS

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