Use a GCD dispatch queue. It automatically provides FIFO ordering, and blocks. Although you can not cancel a request that is already running, you can prevent others from running.
Since you did not say you were using CoreData, and you are already using a separate thread, I'll assume you are NOT using CoreData as your "database."
Try something simple like...
Create your queue when your class initializes (or app starts if its always supposed to be there)...
dispatch_queue_t workQ = dispatch_queue_create("label for your queue", 0);
and release it when your class deallocs (or other appropriate time)...
dispatch_release(workQ);
To get a semblance of cancels, if another selection has been made, you can do something simple like using a token to see if the request you are working on is the "latest" or whether another request has come along since then...
static unsigned theWorkToken;
unsigned currentWorkToken = ++theWorkToken;
dispatch_async(workQ, ^{
// Check the current work token at each step, to see if we should abort...
if (currentWorkToken != theWorkToken) return;
MyData *data = queryTheDatabaseForData(someQueryCriteria);
if (currentWorkToken != theWorkToken) return;
[data doTimeConsumingProcessing];
for (Foo *foo in [data foo]) {
if (currentWorkToken != theWorkToken) return;
// Process some foo object
}
// Now, when ready to interact with the UI...
if (currentWorkToken != theWorkToken) return;
dispatch_async(dispatch_get_main_queue(), ^{
// Now you are running in the main thread... do anything you want with the GUI.
});
});
The block will "capture" the stack variable "currentWorkToken" and its current value. Note, that the unlocked check is fine here, because you do not need to keep track of the continuous count, just if it has changed since you set it. In the worst case, you will do an extra step.
If you are using CoreData, you can create your MOC with NSPrivateQueueConcurrencyType, and now you don't need to create a work queue at all because the private MOC has its own...
static unsigned theWorkToken;
unsigned currentWorkToken = ++theWorkToken;
[managedObjectContext performBlock:^{
// Check the current work token at each step, to see if we should abort...
if (currentWorkToken != theWorkToken) return;
MyData *data = queryTheDatabaseForData(someQueryCriteria);
if (currentWorkToken != theWorkToken) return;
[data doTimeConsumingProcessing];
for (Foo *foo in [data foo]) {
if (currentWorkToken != theWorkToken) return;
// Process some foo object
}
// Now, when ready to interact with the UI...
if (currentWorkToken != theWorkToken) return;
dispatch_async(dispatch_get_main_queue(), ^{
// Now you are running in the main thread... do anything you want with the GUI.
});
}];
GCD/Blocks is really the preferred method for this kind of stuff.
EDIT
Why is it preferred? Well, first off, using blocks allows you to keep your code localized, instead of spread out into other methods of yet another class (NSOperation). There are many other reasons, but I'll put my personal reasons aside, because I was not talking about my personal preferences. I was talking about Apple's.
Just sit back one weekend and watch all the WWDC 2011 videos. Go ahead, it's really a blast. I mean that with all sincerity. If you are in the USA, you've got a long weekend coming up. I bet you can't think of something better to do...
Anyway, watch those videos and see if you can count the number of times the different presenters said that they strongly recommend using GCD... and blocks... (and Instruments as another aside).
Now, it may all change really soon with WWDC 2012, but I doubt it.
Specifically, in this case, it's also a better solution than NSOperation. Yes, you can cancel an NSOperation that has not yet started. Whoopee. NSOperation still does not provide an automatic way to cancel an operation that has already begun execution. You have to keep checking isCanceled, and abort when the cancel request has been made. So, all those if (currentToken != theWorkToken) will still have to be there as if ([self isCancelled]). Yes, the latter is easier to read, but you also have to explicitly cancel the outstanding operation(s).
To me, the GCD/blocks solution is much easier to follow, is localized, and (as presented) has implicit cancel semantics. The only advantage NSOperation has is that it will automatically prevent the queued operation from running if it was canceled before it started. However, you still have to provide your own cancel-a-running-operation functionality, so I see no benefit in NSOperation.
NSOperation has its place, and I can immediately think of several places where I would favor it over straight GDC, but for most cases, and especially this case, it's not appropriate.