0

I'm using an NSThread as follows

NSThread * thread = [[NSThread alloc] initWithTarget:object selector:@selector(bg) object:nil];
[thread start];

Later on I want to stop the thread and deallocate object as follows:

[thread cancel];
[object release];

This seems to work ok. However, when I look at the leaks tool I'm seeing some mysterious leaks not coming anywhere from my code (an empty NSArray). When I looked at the malloc history I see that the NSArray is being allocated in a "willChangeValueForKey" method that is being called ultimately from my [object dealloc]. It happens to be setting a delegate to nil. That delegate is being observed (therefore the willChangeValueForKey?). The [object dealloc] is called from an [NSThread exit].

My guess was that this is because the [thread cancel] doesn't spin down the thread right away (it's in a different thread after all). And then we release object on the main thread. This leaves its retainCount at 1. Then the NSThread will release object when it does actually spin down. It appears that this causes the leak. I tried this quick change to validate my assumption:

[thread cancel];

[NSThread sleepForTimeInterval:1];
// This makes it wait until the thread releases [object]

[object release];

Question: why is it unsafe to let the NSThread deallocate my object? Does it have to do with the observer code being unsafe in dealloc?

George
  • 1,457
  • 11
  • 26
  • Are your `willChange`/`didChange` happening on the same thread that `addObserver:` and `removeObserver:` are being called on? – iluvcapra Feb 15 '13 at 00:52
  • Does your thread have its own autorelease pool? – Hot Licks Feb 15 '13 at 02:24
  • autoreleasepool: yes. As for iluvcapra's question: I'm not sure. This particular piece of code is pretty complicated so I'm not 100% sure. Could having them on different threads cause a leak? – George Feb 15 '13 at 08:59
  • You could conceivably have a race where one thread is doing an observation routine while another is removing it as an observer. – iluvcapra Feb 15 '13 at 21:04

1 Answers1

1

Your main thread should be able to [object release] immediately, you don't have to wait until you cancel the thread, or even until you've launched it. initWithTarget: implicitly retains object and cancel implicitly releases it.

You don't have to retain object on the main thread in order to keep it alive for the detached thread's sake.

iluvcapra
  • 9,436
  • 2
  • 30
  • 32
  • There are other reasons I need to keep `object` around. But I'm also not sure that this would help. It seems that it messes up when the release happens from the thread that I launched. – George Feb 15 '13 at 08:58
  • I refactored a bit to make it possible for `object` to not hold on to too much stuff so it's safe to dealloc it whenever. I found that the problem was really that `object` was holding onto another object that was observing itself. Based on some research, it seems that observing self is not a good pattern. So that's probably the real fix here and will be what I do eventually. Though factoring out the NSThread specific stuff is reasonable anyway. Thanks for the help. – George Feb 15 '13 at 21:27
  • I've observed `self` before and it works, but observing across thread boundaries works very poorly. – iluvcapra Feb 16 '13 at 17:05