Proper usage of NSInvocationOperation and NSOperationQueue - iphone

I have set up an operation queue and an invocation operation. Do I need to signal that the invocation is commpleted? If not how will the operation queue knows the invocation is finished and move on to the next one? The operation queue has been set to execute one operation at a time.

No, there is no need to signal that the invocation has been completed. An NSOperationQueue knows that an operation is finished when its isFinished property is set to YES. This happens by default when the operation's -main method returns.
NSInvocationOperation's -main method, for all intents and purposes, just invokes its NSInvocation and returns, so its isFinished flag should be set to YES immediately after the invocation completes.

Boon,
It seems like what you really want here is to subclass NSOperation yourself and call your asynchronous inside of it. When the async code completes and you get your callback, you would then notify the queue via KVO that isExecuting and isFinished are updated. This is explained much more in detail over at Dave Dribin's blog:
http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/

It's automatic for NSInvocationOperation. You're already good to go.
If you need to tell other parts of your app that the operation has completed, you can use a notification. Be sure the notification goes to the right thread. On the iPhone, I send them to the main thread because I often change the UI in response to a notification, and all UI stuff must happen on the main thread.
[self performSelectorOnMainThread:#selector(postOpDoneNote) withObject:nil waitUntilDone:NO];
-(void) postOpDoneNote
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"someOpDone" object:self];
}

Related

Elegant way to prevent method from being called by different entities simultaneously

I have a method, someMethod, that is called when a) the user taps the view and b) when a user drags the view. In someMethod, there is a UIView animateWithDuration block that makes the toolbar on top of the view disappear, and resets its frame accordingly. If the user taps the view, than drags it, someMethod will be fired while the animation is still completing, and this isn't the behavior I want (simply canceling the animation doesn't work because the completion block still fires (even if I check the 'finished' BOOL). All things being considered, I just don't want this method to be fired while the animation is still in progress.
Obviously an easy solution to this is to set a manual lock with a BOOL and only allow the method to be called once the lock is free.
I'm wondering, is there a more elegant way to accomplish this? Possible to use GCD or some other library to accomplish this so it's more fool proof?
Update: I did try to use synchronized, the problem though is the method fires off the animation, finishes, but the animation is still running on another thread. Any other ideas?
A timer running out does not imply or require a secondary thread. You're in control of what thread a timer is scheduled on. If you just schedule the timer on the main thread, then both things happen on the main thread.
The suggestions of using #synchronized achieve the effect that a given block of code is not running for the same object (whatever is the parameter of #synchronized) at the same time, but that's not the same thing as saying it's not run on two different threads.
If you want to detect if a method is called on a thread other than the main thread and then shunt it over to the main thread, you can use +[NSThread isMainThread] and dispatch_async(dispatch_get_main_queue(), ^{ /* re-call current method */ });.
In modern iOS and OS X, the most elegant mechanism for controlling the execution is to use dispatch queues and blocks. For a global lock, you can use a single serial queue and make request to it either synchronously or asynchronously, depending on whether you want the remainder of the execution on that thread to stop while you execute the critical code.
Declare your queue globally somewhere:
dispatch_queue_t myQueue;
So, when you launch, you'll create your queue:
myQueue = dispatch_queue_create( "CRITICAL_SECTION", DISPATCH_QUEUE_SERIAL); // FIFO
And when you want to execute the critical section of code, you use:
dispatch_sync( shpLockQueue, ^{
// critical section here
});
Depending on your needs, you might want to call your method within one of these blocks, or you might want to have the block within the object that you are protecting.
You could use the main dispatch queue for this, if you needed to make sure that the routine is run on the main thread, but if that's unnecessary, it's going to be more efficient to use your own queue. If you elect to use the main queue, you don't need to set up your own queue, or store it, you can just execute your code within:
dispatch_sync( dispatch_get_main_queue(), ^{
// critical section here
});
I would suggest the #synchronized() block, Heres a great blog post on the explanation of it:
http://googlemac.blogspot.com/2006/10/synchronized-swimming.html
#synchronized(self) {
[self someMethod];
}
Well even using just a global variable, doesn't guarantee mutual exclusion, since the variable is copied to the register before being updated, if that indeed is what you meant by "manual lock BOOL ..." and unfortunately their aren't any really elegant solutions ....
Check out https://developer.apple.com/library/mac/ipad/#documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html
Good luck.

How can I cancel an NSOperation in the same thread that the operation?

When I cancel an NSOperation (when user presses a button) cancel method is called from the main thread, but evidently the operation is running in another thread.
So, to avoid race conditions when I change _isExecuting and _isFinished, I think cancel (or at least its logic) should be called from the same thread that the NSOperation. Apart from that, when user cancels it, several files are deleted and it takes time. Because cancel is called from main thread, all the app becomes unresponsive for a while, which is ugly.
How can I execute cancel code in the same thread that the current NSOperation?
I tried this in cancel (similar to what I saw in ASIHTTPRequest):
if (_operationThread) {
[self performSelector:#selector(cancelOnRequestThread) onThread:_operationThread withObject:nil waitUntilDone:NO];
} else {
[self cancelOnRequestThread];
}
And _operationThread is setted in start method using:
_operationThread=[NSThread currentThread];
But it doesn't work.
Any idea or suggestion?
Note: I use concurrent operations, so I use start instead of main.
Thanks a lot for help.
Ricardo.
It's fine to call cancel on an NSOperation from the main thread. The cancel method is thread-safe.
That shouldn't cause any blocking on your main thread because the cancel method itself shouldn't be doing any work. If you have overridden the cancel method of your operation to delete files, etc then that is the wrong approach. You shouldn't override the cancel method, instead just check the isCancelled method at regular points within the operation's main method (e.g. inside any tight loops) and then return from main early if isCancelled returns YES, which will then cancel the operation on the same thread as the rest of the execution.
If that's how you've implemented it already and you're still having performance issues, is it possible that your operation is not really running on a background thread at all? For example if you've added it to the queue returned by [NSOperationQueue mainQueue] then that's actually running on the main application thread.

NSOperation finishes in the background, attempts to notify main thread, view no longer exists. Crash

I have an NSOperation running not in the main thread. It is spawned from a UITableViewController. When the operation is complete, I'd like to reload the tableview since some data has changed. I've set a delegate for the background to notify on completion. When done, I call a wrapper around reloadData specifically on the main thread using performSelectorOnMainThread.
For the most part, this works well, however, there is a non-0 chance that the original (edit)tableViewController (/edit) gets released and I get zombie calls.
So the question is in 2 parts:
Is it possible to have a delegate from the background thread without retaining the object?
Is this just a bad design? Should I be using NSNotifications instead? Would that be the preferred method of notifying in this case?
Thanks in advance.
A delegate should be retained if there is a possibility that it might be released before any operation on the delegate is invoked. You can set up a state in tableViewController to handle the case when the delegate callback is invoked and the tableViewController is not to be used (Basically make the callbacks act as no-op). Once your operation is done, just release the delegate object.
It is not a bad design but you just need to handle these conditions.

how to stop nsthread

I am using a thread to update messages in background in my application. The thread is started in my messages class.
Messages.m
timerThread = [[NSThread alloc] initWithTarget:self
selector:#selector(startTimerThread:) object:nil];
[timerThread start];
now I want the thread to stop when the user signout of the application. For that in the signout method (in another class) I added
Messages *msg=[[Messages alloc]init];
[msg.timerThread cancel];
[msg release];
But even after signing out of the application the thread is still running.
[timerThread cancel] doesn't stop thread, it just marks it as cancelled. I presume that your startTimerThread: performs some sort of infinite loop. You have to check isCancelled in this loop periodically and if it's YES you should perform some clean up and break from this loop.
BTW if you don't perform updates frequently it is more convenient to run NSTimer in main thread and detach new thread on callbacks (like [self performSelectorInBackground:#selector(updateMessages:) withObject:whatever]) or start an instance of NSOperation.
( I am assuming here that you create an instance of Messages somewhere else in the program )
According to your provided code you are creating a new Messages instance as part of your sign out process ( which starts a new thread ), canceling that new thread ( -cancel is a proper way to stop a thread -- if your thread method follows the considerations listed in the documentation ), then releasing your ( extra? ) Messages instance. At this point your original Messages instance is still around, and the thread from that is still running.
If you want the thread to stop when the instance of the class is deallocated, you probably should take care of that in a -dealloc method on Messages and make sure your original Messages instance is released at the proper time.
[NSThread exit];
Have you checked this method of NSThread class.
Use [NSThread exit]; or [NSThread cancel];

performSelectorOnMainThread works but performSelector doesn't why?

I have a selector and target, and calls the method like this
[target performSelectorOnMainThread:(SEL)selector withObject:nil waitUntilDone:FALSE];
But after I changed it to this, it doesn't work
[target performSelector:(SEL)selector withObject:nil afterDelay:0];
Any ideas?
I don't want to perform that task on the main thread because it lags the UI.
By doesn't work I mean that it simply doesn't call the method. I have it im debu mode in simulator and confirmed that it was not called.
I assume you're sending the message from another than the main thread. Cocoa just builds a run loop for the main thread, for other threads you have to build one yourself. The method performSelector:withObject:afterDelay: schedules the message for the next pass through the run loop. So if there is none, the message will not be sent.
For your case, why don't you just send [target performSelector:selector withObject:nil];? You dont need a run loop for that and the message will be sent immediately (on the same thread).