how to stop nsthread - iphone

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

Related

iPhone SDK Background threads calling other methods

I am a seemingly straightforward question that I can't seem to find an answer to (and it is hindering my app).
I have a background thread running a paricular method:
-(void)processImage:(UIImage *)image {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//Process image here in the background here
[pool drain];
}
This much works great, but my question comes when I want to call another method from inside the already-background method. Does this call stay in the background? Do I need to add NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; and [pool drain]; to the new method to make it run in the background as well?
Any advice would be very helpful. I am a bit confused about this.
Many thanks,
Brett
It WILL stay in the background, on the same thread it was called from.
Some threading notes to consider with this:
It may not be obvious, but if you call a timer from the background thread, and the thread exits before the timer is supposed to go off, the timer will NOT be called. Thus it is recommended you setup timers from the main thread
You dont need another autorelease pool unless you spawn another thread.
Any UI updates should be done on the main thread
You don't need to add yet another autorelease pool, the one you already have is enough. And yes, all calls that you make that originate from that thread stay in that thread and thus also run "in the background". Exception would be the use of "performSelectorOnMainThread:", which of course makes the given selector to be performed on the main thread :-) If you want to call GUI methods (like setting the image on an UIImageView) you should make sure to do so on the main thread. See the docs for "performSelectorOnMainThread:waitUntilDone:" (sorry for not giving you you the links, am typing this on my iPad).

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

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

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

performSelectorInBackgroundThread and NSTimer

For a game I'm developing, I call an expensive method from one of the touch processing routines. In order to make it faster, I decided to use performSelectorInBackgroundThread, so instead of:
[gameModel processPendingNotifications];
I switched to:
[gameModel performSelectorInBackground:#selector(processPendingNotifications) withObject:nil];
The first problem I had, is that processPendingNotifications did not have a NSRunLoop, so I added it, like this:
- (void)processPendingNotifications {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pendingNotificationsQueue makeObjectsPerformSelector:#selector(main)];
[pendingNotificationsQueue removeAllObjects];
[pool drain];
}
So far so good. My problem is that some of the methods that are called from this background thread create new NSTimer instances. These instances, end up not firing. I think this is because the secondary thread I have doesn't have a (or is ending its) NSRunLoop. I'm starting the new timers by using:
[NSTimer scheduledTimerWithTimeInterval:20.0 target:self selector:#selector(timerFired) userInfo:nil repeats:NO];
My questions are:
Am I on the right path of suspecting the problem has to do with the NSRunLoop ?
Is there a way I can start a NSTimer from a background thread and attach it to the main thread's NSRunLoop ?
Yes, you need a runloop to dispatch the timer events
NSTimers are implicitly attached to the runloop of the thread they are created on, so if you want it to be attached to the main thread's runloop create it on the main thread using performSelectorOnMainThread:withObject:waitUntilDone:. Of course that code will execute on the main thread.
If your question is "Can I have a timer run on the main thread's run loop that directly runs a selector on another thread?" The answer is no, but the selector it fires on the main thread could just performSelector:onThread:withObject:waitUntilDone:. Of course that requires the thread you are trying to perform the selector against have an operational runloop.
If you want the code the timer is triggerring running on a background thread then you really need to get the background thread's runloop going, and if you do that then you don't need to schedule anything with the main thread since you will have an operational runloop.
The NSTimers actually just periodically fire events into the enclosing NSRunLoop, which each thread has (or should have). So, if you have a child (or background) process running in a different thread, the NSTimers will fire against that thread's NSRunLoop instead of the application's main NSRunLoop.
You could ensure that timers are always created against the main runloop by sending it the addTimer:forMode: message with your newly instantiated (but not started) NSTimer. Accessing the main application's run loop is done with [NSRunLoop mainRunLoop], so regardless of which thread you're in, doing [[NSRunLoop mainRunLoop] addTimer:[NSTimer timerWithTimeInterval:20.0 target:self selector:#selector(timerFired) userInfo:nil repeats:NO]] forMode:NSDefaultRunLoopMode] will always schedule the timer against the main runloop.
However, bear in mind that the execution is not guaranteed at that interval, and if your main loop is busy doing something your timer will be left waiting until it's ready.
It's worth considering NSOperation and NSOperationQueue if you really want background activity to occur.