开发者

Memory management with NSThread

I have an app that needs to signal continuously a word in morse code. I did this by creating an NSThread and running some code inside the selector with a "while loop". Here is the code:

@implementation MorseCode


-(void)startContinuousMorseBroadcast:(NSString *)words{
    if (!(threadIsOn)) {

    threadIsOn = YES; s

    myThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadSelector:) object:words];

    [myThread start];

}

if (morseIsOn) {
    morseIsOn = NO;
}
else{
    morseIsOn = YES;
}


   }

-(void)threadSelector:(NSString *)words{  

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

while (![myThread isCancelled]) {

//    ///it Does some code here


} //end While


NSLog(@"Cleaning the pool");
    [pool drain];
}


@end

When exiting the application (the user presses the button), in the applicationDidEnterBackground the following selector is executed:

-(void)cleanUpMorseObject{ //this is defined in the MorseCode class, same as threadSelector
    if (threadIsOn) {

        NSLog(@"cleanUpMorseObject, threadIsOn");
        threadIsOn = NO;
        morseIsOn = NO;

        [myThread canc开发者_Python百科el];
        [myThread release];

    }
}

The application responds correctly to the event, I’ve checked with nslog. And then [MorseCode release] is called. The code looks like this:

-(void)applicationDidEnterBackground{ //this happens in the ViewController
[theMorse cleanUpMorseObject]; //theMorse is an instance of MorseCode
[theMorse release];
}

The problem: Although I call [myThread release] and then [theMorse release] the retainCount of the theMorse is still above 0 (It doesn’t call the dealloc).

The Leaks Instrument doesn’t say I have a leak, but if I open and close the application for like 10 times eventually the Iphone resets. Also in the debugger eventually I see the “Received memory warning. Level=2”.

Also I never see the NSLog before the pool drain…

The app doesn't run in the background. Any ideas? Thank you


You really should schedule the sending of the message on the RunLoop, the probably easiest way being to schedule a timer (repeat infinitely, and short repeat period like FLT_EPSILON or similar) instead of using threads for that.

Working with threads is complicated and as everyone should avoid it (as Apple stated in its Concurrency Programming Guide, and as most documentation said, "Threads are evil" ;)).

That's because multithreading is a vast and complicated subject, that needs synchronizations, resources protection, being aware of dead locks, critical sections & so on, good and adapted memory mgmt, and much much more. In general if you need to do stuff in the background:

  • Use mechanisms already in place (like asynchronous implementation of some operations and being signalled by delegate methods or notifications) if available
  • Use methods like performInBackground:
  • Use NSOperationQueues
  • Use GCD
  • And only in last resort and if there are no other options (or for really specific cases), use NSThread.

This will avoid you a lot of issues as all the other, higher APIs will take care of a lot of things for you.


Moreover, using threads for this task like you do is likely to use much more CPU (will probably reach 100% usage quickly) as there won't be any time left for the task scheduler (that also why even GCD that takes care of all stuff like that is way better than NSThreads, and scheduling the sending in the RunLoop is even better for the CPU if you don't need strong RT constraints)


First, retainCount can never return 0. It is a useless method. Don't call it.

Secondly, leaks only detects objects that are no longer referenced. If a thread is still running, it isn't leaked.

Finally, a thread doesn't stop when you call cancel. It just sets a flag that you have to check via isCancelled to see if it is time to stop work in the thread. Are you doing that?


OK -- easy stuff answered. Next? Try build and analyze. Then use the Allocations instrument and turn on reference count tracking. Then see what is calling retain an extra time.


I decided to give up the NSThread class and used another aproach:

-(void)playSOSMorse{

    if ([myTimer isValid]) {

        [myTimer invalidate];
        [myTimer release];
        myTimer = nil;
    }

        myTimer = [[NSTimer scheduledTimerWithTimeInterval:0.001
                                                    target:self
                                                  selector:@selector(tymerSelector)
                                                  userInfo:nil
                                                   repeats:NO] retain];
    //the timer calls a selector that performs a selector in background

}

-(void)tymerSelector{

    [self performSelectorInBackground:@selector(threadSelector2) withObject:nil];  
}

-(void)threadSelector2   {

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

//some code here

 [pool drain];

//calls another selector on the main thread to see if it needs to fire the timer again and restart the cycle
 [self performSelectorOnMainThread:@selector(selectorOnMainThread) withObject:nil waitUntilDone:NO];

}

-(void)selectorOnMainThread{
        [myTimer invalidate];
        [myTimer release];


    myTimer = nil;


    if (morseIsOn) { //this is a boolean that if it is true (YES) calls the timer again

        [self playSOSMorse];  

    }

}

I hope this helps somebody :) Thank you

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜