performSelectorOnMainThread works but performSelector doesn't why? - iphone

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).

Related

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.

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];

iphone - what is the logic of this kind of multithread operation

I am just trying to understand this: multithread is a way of firing a method in another thread, so it will not hang the main thread (when something heavy must be done). But I have seen stuff like this:
this goes on the main code:
[NSThread detachNewThreadSelector:#selector(createNewThread) toTarget:self withObject:nil];
and here goes the thread
-(void) createNewThread {
NSAutoreleasePool* p = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(doSomeStuff) withObject:nil waitUntilDone:YES];
[p release];
}
My problem with this is: if the thread is intended to relieve the main thread from doing something, why is doSomeStuff being called using performSelectorOnMainThread? Isn't that insane? I mean, you fire a second thread just to run something on the main thread, a thread that in fact you shouldn't be using.
I have seen a lot of code on the web write this way.
I know that probably it is done that way because it is doing something that is not thread safe, but is doing this really useful? To me sounds that it is totally useless and it would be better to do something like
[self performSelector:#selector(doSomeStuff) withObject:nil afterDelay:0.01];
what do you guys think?
thanks in advance.
performSelectorOnMainThread:withObject:waitUntilDone: puts the call on a queue to perform on the next iteration of the run loop. performSelector:withObject:afterDelay: sets up a timer to perform the action. However, creating a new thread to call performSelectorOnMainThread makes no sense because it puts the call on the queue even if it is already on the main thread.
If that's the entire body of the createNewThread method then it makes no sense. But if other stuff actually happens in createNewThread then there might be a good reason. The most common scenario for having a background thread use performSelectorOnMainThread: is if the UI needs to update as a result of the background activity. Like if you're processing a large data set and you want to show the user how much progress has been made so far. UIKit is not thread-safe so you can't update the UI from the background thread. But you might do something like, have the background doing a lot of work in a loop, and at every pass through the loop (or every 5, or whatever) call back to the main thread to update the UI. This makes the code switch back to the main loop just long enough to update the UI before returning to the background for the heavy duty work.

performSelectorInBackground: on main thread

I know this is a wack question, but it is valid to performSelectorInBackground: on an iPhone apps' main thread? I am aware of performSelectorOnMainThread: but I was just wondering if performSelectorInBackground: can also be used on the main thread. My understanding is it cannot, because performSelectorInBackground: spawns a new thread each time.
performSelectorInBackground: essentially spawns a new thread, then performs the desired selector on that thread.
So, no it does not execute on the main thread. Ever.
I think that using performSelector:withObject:afterDelay: without a delay would be appropriate for your situation, because that does perform the selector on the main thread, except that it's performed in the next iteration of the current run loop:
[self performSelector:#selector(someMethod) withObject:nil afterDelay:0];

Proper usage of NSInvocationOperation and NSOperationQueue

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];
}