(iphone) having NSThread* as member variable is a bad idea? - iphone

One of my custom class has NSThread* as member variable.
I'm letting the thread exit by setting a variable(isNeedToExit).
Then call a release on the thread object [myThread release];
I intended the thread to exit first and release call gets called.
But on second thought, release call might get called before the thread notices the boolean value changed and exit.
(Because the thread is not constantly looking at the value to see if it needs to exit)
#property (nonatomic, retain) NSThread* myThread;
- (void) dealloc
{
...
[myThread release];
myThread = nil;
...
[super dealloc];
}
- (id) initMyClass
{
if(self = [super init] )
{
...
NSThread* aThread = [[NSThread alloc] initWithTarget: self selector:#selector(theThreadMain) object: nil];
self.myThread = aThread;
[aThread release];
[self.myThread start];
..
}
return self;
}
- (void) theThreadMain
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// Add your sources or timers to the run loop and do any other setup.
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
do
{
// Start the run loop but return after each source is handled.
SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES);
}
while (self.isNeedToExit == false);
[pool release];
SYSLOG(LOG_DEBUG, "thread exiting");
}
I thought of moving [myThread release] call (now at the class's dealloc) to last line of theThreadMain..
Not sure if this is the correct way to stop a thread when it is a member variable of another class.
Thank you

Actually, your initial assumption was more correct. Your current implementation, however, will not work because dealloc will never be called.
Your class is retained by NSThread in initWithTarget:selector:object and will not be released until the thread finishes executing. Therefore, your class instance will not be deallocated as long as the thread is executing.
One way to solve the problem is to add a "cancel" method to your class that stops the thread. E.g.:
- (void)cancel
{
isNeedToExit = YES;
[myThread release];
myThread = nil;
}
That will cause the thread to stop and will allow your class to be deallocated (once the thread stops.) You need to ensure that all users of your class know that "cancel" needs to be called before the class is released. E.g.:
[myObject cancel]; // Stop the thread
[myObject release];
myObject = nil;
A more traditional solution would be to simply subclass NSThread.

Related

How to cancel or stop NSThread?

I'm doing an app that loads the contents of viewControllers using NSThread while is reading an XML file.
I have it done as follows:
-(void)viewDidAppear:(BOOL)animated
{
// Some code...
[NSThread detachNewThreadSelector:#selector(loadXML) toTarget:self withObject:nil];
[super viewDidAppear:YES];
}
-(void)loadXML{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Read XML, create objects...
[pool release];
}
My problem is that I don't know how to stop the NSThread if the user changes to another viewController while the NSThread is loading, doing that the app crashes.
I've tried to cancel or exit the NSThread as follows but without success:
-(void)viewsDidDisappear:(BOOL)animated{
[NSThread cancel];
// or [NSThread exit];
[super viewDidDisappear:YES];
}
Can anyone help? Thanks.
When you detach new thread, you can no more cancel or exit it from viewDidDisappear etc. These UI specific methods execute only on main thread so the exit/cancel applies to the main thread which is obviously wrong.
Instead of using the detach new thread method, declare NSThread variable in .h and initialize it using initWithTarget: selector: object: method and cancel it whenever/wherever you want to..
you can also use [NSThread exit]; method of NSThread.
It's better to let a thread end gracefully, i.e. reach its natural conclusion, if you can. It sounds like in your case you can afford to. Also be sure that you're updating the user interface from the main thread, not a secondary thread, as UIKit is not thread safe.
You wrote:
... the app stops responding while the thread finishes...
Once you flag a thread for cancelling or exit, you have to manually stop whatever the thread was called to do. An example:
....
- (void) doCalculation{
/* Do your calculation here */
}
- (void) calculationThreadEntry{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSUInteger counter = 0;
while ([[NSThread currentThread] isCancelled] == NO){
[self doCalculation];
counter++;
if (counter >= 1000){ break;
} }
[pool release]; }
application:(UIApplication *)application
- (BOOL)
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
/* Start the thread */
[NSThread detachNewThreadSelector:#selector(calculationThreadEntry)
toTarget:self withObject:nil];
// Override point for customization after application launch. [self.window makeKeyAndVisible];
return YES;
}
In this example, the loop is conditioned on the thread being in a non-cancelled state.

how to properly use autoreleasepool for an nsoperationqueue

I have an app that I am refactoring and I just implemented multithreading so that the UI may run smoother. In the iphone simulator I don't get any leaks but testing on my iPhone 3G running on iOS 4.2 I get a memory leak. I have done a lot of searching for the correct way to implement an autoreleasepool with an operationqueue, help will be greatly appreciated.
I have created an nsoperationqueue in my viewcontroller as such
- (void)loadData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSOperationQueue *queue = [NSOperationQueue new]; // creates multithread for loading data without slowing UI
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(firstRun) object:nil];
[queue addOperation:operation];
[operation release];
[queue release];
[pool release];
}
First, you shouldn't just create and then release the queue. It's more natural to create that queue as one of your class's ivars and then release it when your view controller goes away (you can also cancel up any pending operations and cancel/wait for any running operations to complete).
Second, you don't need the autorelease pool in the method that creates the operation and adds it in the queue since that method is being called from the main thread. Instead, you need the autorelease pool from the method your object actually calls (this is what may be running on another thread).
So, you might have the following (assuming you name your queue ivar queue_):
- (void)viewDidLoad
{
[super viewDidLoad];
if( !queue_ ) queue_ = [[NSOperationQueue alloc] init];
// other view loading code
}
- (void)loadData
{
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(firstRun) object:nil];
[queue_ addOperation:operation];
[operation release];
}
- (void)firstRun
{
// Here we may run on another thread, so 1) we need an autorelease pool; and
// 2) we need to make sure we don't do anything that requires a runloop
NSAutoreleasePool* threadPool = [NSAutoreleasePool new];
// do long-running things
[threadPool drain];
}
- (void)dealloc
{
if( queue_ ) {
[queue_ setSuspended:YES];
[queue_ cancelAllOperations];
// you need to decide if you need to handle running operations
// reasonably, but don't wait here because that may block the
// main thread
[queue_ release];
}
// other dealloc stuff
[super dealloc];
}
You could also initialize your queue on demand, so instead of initializing in viewDidLoad, check for its existence and initialize if necessary anywhere you'd add an operation. This might be wrapped in a method call of its own, but lazy initialization here probably isn't really necessary as queues are pretty light-weight.
You should create an NSAutoreleasePool at the start of the method that the NSOperation will invoke (in this case, firstRun), and drain it at the end of the method.

Searching using NSOperation

I am trying to make a searchbar to search for things I receive using NSURLConnection.
right now, if I search for something, that string is send away as an URL with an asynchronous request, which gives me data.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:urlString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
[theConnection cancel];
[theConnection release];
theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
That data is parsed and when it is successful I post a notification
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
xmlParser = [[NSXMLParser alloc] data];
[xmlParser setDelegate:xmlGeocoder];
BOOL success = [xmlParser parse];
if(success == YES){
NSLog(#"No Errors");
[[NSNotificationCenter defaultCenter] postNotificationName:#"getArray" object:self];
}else{
NSLog(#"Error Error Error!!!");
[[NSNotificationCenter defaultCenter] postNotificationName:#"failToGetArray" object:self];
}
}
and my searchresultsTableView is reloaded.
self.array1 = [array2 copy];
[self.searchDisplayController.searchResultsTableView reloadData];
All these methods are depending on eachother, so B can't be executed, when A is still busy.
I am using NSNotificationCenter to tell them to execute those code.
But I want to try NSOperation and I have no idea HOW to implement that.
Do I have to put my search requests in an operation or every method I'm using?
Can someone give me a sample code to give me the idea how this should be done?
Thanks in advance...
NSOperation is very useful. To use it you extend NSOperation and override the "main" method.
In the main method you do your calculations/web request etc. So NSOperation is best for tasks you can wrap into a few simple steps, after each step you test if everything is good and either continue to the next step or cancel the operation. Once this is done you can simply instantiate your custom NSOperation and hand it off to a NSOperationQueue object and it will take care of the threading, starting, stopping cleaning up etc.
In the example below I have written a protocol to handle the completion of the task, I would advise you take this approach instead of using notification - unless you have multiple objects that needs to be notified instantly.
Make a new class that extends the NSOperation class:
//This object takes a "searchTerm" and waits to be "started".
#import <Foundation/Foundation.h>
#protocol ISSearchOperationDelegate
- (void) searchDataReady:(NSArray*) searchResult;
#end
#interface ISSearchOperation : NSOperation {
id <ISSearchOperationDelegate> delegate;
NSString *searchTerm;
}
#property(nonatomic, retain) NSString *searchTerm;
#property(nonatomic, assign) id delegate;
- (id) initWithSearchTerm:(NSString*) searchString;
#end
When an object extending NSOperation is added to an NSOperationQueue, the queue object
tries to call a "main" method on the NSOperation, you must therefore wrap your task in this method.
(notice that after each completed sub-task I test if it went well and "return" if not. The NSOperation class
has a property called isCancelled This property can be set by the NSOperationQueue, so you must also
test if that has been set during your completion of main. So to recap, you test from the inside of main if each step went as you wanted and you test if something on the outside has cancelled your task.):
- (id) initWithSearchTerm:(NSString*) searchString {
if (self = [super init]) {
[self setSearchTerm:searchString];
}
return self;
}
- (void) main {
[self performSelector:#selector(timeOut) withObject:nil afterDelay:4.0];
if ([self isCancelled]) return;
NSData *resultData = [self searchWebServiceForString:self.searchTerm];
if (resultData == nil) return;
if ([self isCancelled]) return;
NSArray *result = [self parseJSONResult:resultData];
if ([self isCancelled]) return;
if (result == nil) return;
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[delegate performSelectorOnMainThread:#selector(searchDataReady:) withObject:result waitUntilDone:YES];
}
//I have not copied the implementation of all the methods I call during main, but I hope you understand that they are just "tasks" that each must be successfully completed before the next sub-task can be computed.
So first of I put a timeout test in there, then I get my data from the web service and then I parse it.
Ok to get all this going you need a queue.
So in the class you want to be the delegate for this operation you do this:
somewhere set up a queue:
NSOperationQueue *q = [[NSOperationQueue alloc] init];
[self setQueue:q];
[q release];
- (void) doSearch:(NSString*) searchString {
[queue cancelAllOperations];
ISSearchOperation *searchOperation = [[ISSearchOperation alloc] initWithSearchTerm:searchString];
[searchOperation setDelegate:self];
[queue addOperation:searchOperation]; //this makes the NSOperationQueue call the main method in the NSOperation
[searchOperation release];
}
//the delegate method called from inside the NSOperation
- (void) searchDataReady:(NSArray*) results {
//Data is here!
}
Some of the advantages with NSOperations is that from the caller point of view, we simply make an object, set a delegate, wait for the reply. But behind the scenes a series of threaded tasks that can be cancelled at any time is run, and in a manner that can handle if threaded stuff fails.
As you can see in the doSearch method it starts out by canceling any previous operations, I did this in an app where I would search a web service each time a user typed a letter in a word. That means that if the user searched for "hello world" - I would do a search for "h", then "he", then "hel", then hell", then "hello" etc.
I wanted to stop and clean up the "h" task as soon as the user typed the "e", because it was then obsolete.
I found out NSOperation was the only way that gave the responsiveness of threading and none of the mess that usually comes with spawning many threads on top of each other.
Hope you can use it to get started:)

a race condition or not? delegates & multiple threads

I'm puzzling over how multithreading can work with delegates.
The main thread has an object "A", which has created an object "B". Object "A" is the delegate for object "B". Object "B" uses a thread to run the code.
When object "B" wants to notify the delegate, it does:
[[self delegate] performSelectorOnMainThread:#selector(didFinish:) withObject:self waitUntilDone:[NSThread isMainThread]];
The "delegate" property is an assign, atomic #property. Hence it would appear that the generated getter will do [[delegate retain] autorelease], according to objective c manual.
The dealloc method for "A" is:
- (void)dealloc
{
[b setDelegate:nil];
[b release];
[super dealloc];
}
This would appear to lead to the possible situation where the threads run like this:
Main thread: call [A dealloc] (due to a call to [a release])
Other thread: b calls [A retain] (due to the call to [self delegate])
Main thread: calls [b setDelegate:nil]
Other thread: calls performSelectorOnMainThread
At step 2, it would seem that retain cannot succeed, as dealloc is already committed to - is this race condition? What happens if you call retain on an object that is in the process of being dealloced? Can it actually occur?
If it is a race condition, how do multi-threaded objects with delegates usually avoid it?
(This arose from a slightly similar but simpler question/answer I previously asked, how to handle setDelegate with multiple threads.)
Update
It is a race conditon, as the accepted answer proves.
The solution to my original problem is to avoid this case all together, I've updated How to handle setDelegate: when using multipe threads to show this.
I don't think there's a lock on dealloc versus retain/release. The following example has a dealloc method with a sleep() in it (does anyone know if sleep() breaks locks? I don't think it does, but you never know). A better example might be to repeatedly instantiate/destroy instances of A and B until you get a situation like the one mentioned here, without the sleep().
View controller, in my case, but could be anything:
-(void)septhreadRetainDel
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(#"[thread#2] sleep(1.f);");
sleep(1.f);
NSLog(#"[thread#2] [b retainDelegate];");
[b retainDelegate];
NSLog(#"[thread#2] sleep(2.f);");
sleep(2.f);
NSLog(#"[thread#2] [b release];");
[b release];
[pool release];
}
- (void)viewDidLoad {
NSLog(#"-(void)viewDidLoad:");
[super viewDidLoad];
NSLog(#"a = [[A alloc] init];");
a = [[A alloc] init];
NSLog(#"[a autorelease];");
[a autorelease];
NSLog(#"b = [[B alloc] init];");
b = [[B alloc] init];
NSLog(#"b.delegate = a;");
b.delegate = a;
NSLog(#"[NSThread detachNewThreadSelector:#selector(septhreadRetainDel) toTarget:self withObject:nil];");
[NSThread detachNewThreadSelector:#selector(septhreadRetainDel) toTarget:self withObject:nil];
}
A:
#import "A.h"
#implementation A
-(void)dealloc
{
NSLog(#"A: dealloc; zzz for 2s");
sleep(2.f);
NSLog(#"A: dealloc; waking up in time for my demise!");
[super dealloc];
}
-(id)retain
{
NSLog(#"A retain (%d++>%d)", self.retainCount, self.retainCount+1);
return [super retain];
}
-(void)release
{
NSLog(#"A release (%d-->%d)", self.retainCount, self.retainCount-1);
[super release];
}
#end
B (.h):
#import "A.h"
#interface B : NSObject {
A *delegate;
}
-(void) retainDelegate;
#property (nonatomic, assign) A *delegate;
#end
B (.m):
#import "B.h"
#implementation B
#synthesize delegate;
-(void)retainDelegate
{
NSLog(#"B:: -(void)retainDelegate (delegate currently has %d retain count):", delegate.retainCount);
NSLog(#"B:: [delegate retain];");
[delegate retain];
}
-(void)releaseDelegate
{
NSLog(#"B releases delegate");
[delegate release];
delegate = nil;
}
-(void)dealloc
{
NSLog(#"B dealloc; closing shop");
[self releaseDelegate];
[super dealloc];
}
-(id)retain
{
NSLog(#"B retain (%d++>%d)", self.retainCount, self.retainCount+1);
return [super retain];
}
-(void)release
{
NSLog(#"B release (%d-->%d)", self.retainCount, self.retainCount-1);
[super release];
}
#end
The program ends up crashing with EXC_BAD_ACCESS at B's releaseDelegate method. The following is the output from the NSLogs:
2010-07-10 11:49:27.044 race[832:207] -(void)viewDidLoad:
2010-07-10 11:49:27.050 race[832:207] a = [[A alloc] init];
2010-07-10 11:49:27.053 race[832:207] [a autorelease];
2010-07-10 11:49:27.056 race[832:207] b = [[B alloc] init];
2010-07-10 11:49:27.058 race[832:207] b.delegate = a;
2010-07-10 11:49:27.061 race[832:207] [NSThread detachNewThreadSelector:#selector(septhreadRetainDel) toTarget:self withObject:nil];
2010-07-10 11:49:27.064 race[832:4703] [thread#2] sleep(1.f);
2010-07-10 11:49:27.082 race[832:207] A release (1-->0)
2010-07-10 11:49:27.089 race[832:207] A: dealloc; zzz for 2s
2010-07-10 11:49:28.066 race[832:4703] [thread#2] [b retainDelegate];
2010-07-10 11:49:28.072 race[832:4703] B:: -(void)retainDelegate (delegate currently has 1 retain count):
2010-07-10 11:49:28.076 race[832:4703] B:: [delegate retain];
2010-07-10 11:49:28.079 race[832:4703] A retain (1++>2)
2010-07-10 11:49:28.081 race[832:4703] [thread#2] sleep(2.f);
2010-07-10 11:49:29.092 race[832:207] A: dealloc; waking up in time for my demise!
2010-07-10 11:49:30.084 race[832:4703] [thread#2] [b release];
2010-07-10 11:49:30.089 race[832:4703] B release (1-->0)
2010-07-10 11:49:30.094 race[832:4703] B dealloc; closing shop
2010-07-10 11:49:30.097 race[832:4703] B releases delegate
Program received signal: “EXC_BAD_ACCESS”.
Once -dealloc is called, retain counts are no longer of import. The object will be destroyed (this is probably obvious, though I wonder what would happen if you checked self's retainCount and DID NOT call [super dealloc] if the object had retains... insane idea). Now if we modify the -dealloc for A to set B's delegate to nil first, the program works but only because we're nil'ing delegate in B in releaseDelegate.
I don't know if that answers your question, really, but presuming sleep()'s are not somehow breaking thread locks, the exact same behavior should happen when dealloc is called right before a retain.
This is as close to a wild guess as I'm ever going to give on stack overflow, but here goes:
I think that -dealloc is synchronized to the same lock as -retain and -release, which would be crazy to not be atomic. This lock is not gotten magically in dealloc, as clearly that is filled with you're own code, but rather in release, it just holds the same lock while it does its dealloc. (this may be one of the reason's you're not supposed to call dealloc directly)
Now in object B, [self delegate] calls object A's retain, which is, if I'm right, atomic in respect to dealloc and release, and will either occur before -[A dealloc] because it will occur before -[A release], or will happen after -[A dealloc], depending on its timing.
In the first case, where -[A retain] happens before -[A release], the outcome is obvious: object A will not be deallocated until the following -[A autorelease] from the same accessor, and object B will call the delegate method on the still-around object A.
The second case is much trickier, and from this point forth, we shall be leaving the firm foundation of fact and journeying together through the murky marshes of memory into thickets of wildest guesswork. I believe that in the second case, -[A dealloc] attempts to set the delegate of object B (as said before, while the other thread is waiting to acquire the lock on its delegate) to nil. However, with an atomic property, A would then have to acquire the lock B had acquired and was using while waiting for the lock A used for retain/release/dealloc, which is obviously in use.
I think therefore this would cause a deadlock, although again, I am entirely unsure, and this answer is largely based on guesses about what is locked and when. Once again, my only viable solution (but don't stop looking, there must be a better way) is to retain the delegate, at least while the second thread is running, and prevent it from being deallocated in the first place.

iphone threads beginner question, having trouble with managing autorelease pool

I'm having trouble with a simple threading example. I know my methods aren't complete, but as of now, I want to press the start button and have that fire off a thread that increments a counter every second. I know it's connected in IB correctly since the NSLog tells me it gets to my timerThread method. But then it immediately jumps back to the initial myThread, without ever reaching the updateDisplay method and releases the pool, which is why I am guessing my program doesn't actually increment a counter. I thought I would then put it in a sleep interval or something, but in the end I think I am missing the correct way to achieve this. Any thoughts would be great. Thanks!
#implementation MainController
-(id)initWithLabel:(UILabel *)label {
if (self = [super init]) {
countLabel = label;
[countLabel retain];
}
return self;
}
-(int)count {
return count;
}
-(void)setCount:(int) value {
count = value;
}
-(void)updateDisplay:(NSTimer *)timer {
NSLog(#"%s", __FUNCTION__);
countLabel.text = [NSString stringWithFormat:#"%i", count];
count++;
}
-(void)timerThread {
NSLog(#"%s", __FUNCTION__);
[NSTimer timerWithTimeInterval:1.0
target:self
selector:#selector(updateDisplay:)
userInfo:nil
repeats:YES];
//NSNumber *threadID = [NSNumber numberWithInt:(int)threadID];
// threadLabel = [NSString stringWithFormat:#"%#", threadID];
}
-(void)myThread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//NSNumber *threadID = [NSNumber numberWithInt:(int)threadID];
[self performSelectorOnMainThread:#selector(timerThread)
withObject:nil
waitUntilDone:NO];
// NSLog(#"threadID in myThread: %#", threadID);
[pool release];
}
-(void)startThread {
// threadIndex = 0;
// numThreads = 0;
// NSNumber *threadID = [NSNumber numberWithInt:threadIndex++];
[self performSelectorInBackground:#selector(myThread) withObject:nil];
// NSLog(#"%i", threadIndex);
numThreads++;
}
-(void)myThreadStop {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[NSThread exit];
[self performSelectorOnMainThread:#selector(updateDisplay)
withObject:nil
waitUntilDone:NO];
[pool release];
}
-(void)stopThread {
[self performSelectorInBackground:#selector(myThreadStop) withObject:nil];
}
-(void) dealloc {
[countLabel release];
[super dealloc];
}
#end
The short answer is that you did not schedule the timer.
iOS (and others) use run loops. Each thread may have a run loop, and the main UI thread has a run loop set up for you. Simplistically, a run loop keeps a queue of things to do and either does them in an ordered fashion or blocks until there is something to do.
Anything that you do with the active UI, like setting the text of a UILabel, must be done on the main thread. In your case, you have set up (but not scheduled) a timer on the main thread to update the timer. Scheduling a timer just means adding it to the run loop.
If you had a lengthy task to perform that would update the UI at the end, you could use performSelectorInBackground to start the lengthy task and performSelectorOnMainThread when the task finishes to update UI.
If you have a short periodic task, such as updating a clock or counter UI, you can just create an NSTimer on the same thread you want the timer to fire on. When creating the timer, use a scheduledTimerWithTimeInterval variant so it will start firing automatically.
When you create a repeating timer, you must keep a reference to it so you can invalidate the timer before the target is released. At the latest, in dealloc you should invalidate the timer.
Instead of calling startThread, turn timerThread into startTimer.
-(void) startTimer {
timerMember = [[NSTimer
scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateDisplay:)
userInfo:nil
repeats:YES] retain];
}
-(void) dealloc {
[timerMember invalidate];
[timerMember release];
...
}