NSTimer possible crash cause - objective-c

I have an issue while working with NSTimer.
Let's assume I have this architecture :
ThreadedClass.m (contains a NSTimer* timer;)
- (id) init {
if (self = [super init]) {
// do blablabla
[self launchAThread];
}
return self;
}
- (void) launchAThread {
[NSThread detachNewThreadSelector:#selector(selectorToMyThreadFunction)
toTarget:self
withObject:nil];
}
- (void) selectorToMyThreadFunction {
//I do my stuff in here
//Then i relaunch a Timer to call this function
//periodically but it has to be "atomic" so no
//repeating timer since i don't know the time
//this function will take
//I do some [self changeSomething];
[self restartTimer];
//MyThread ends here (and might be recreated by the Timer's bip
}
- (void)restartTimer {
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:#selector(restartTimer)
withObject:nil
waitUntilDone:NO];
return;
}
[timer invalidate];
[timer release];
timer = [[NSTimer scheduledTimerWithTimeInterval:interval
target:self
selector:#selector(launchWithTimer:)
userInfo:nil
repeats:NO] retain];
}
- (void) launchWithTimer:(NSTimer *)theTimer {
if (theTimer == timer)
{
[timer release];
timer = nil;
[self launchAThread];
}
else
{
//Nothing to be done in here, a user launch a thread manually
}
}
So let's assume the user of the class alloc it and release it right after. My timer will still be alive and the object too (since there is a retain made by the timer).
When the timer will fire, it will do [self launchAThread] and then the timer will invalidate and release itself AND it will release my object which now has a retainCount = 0... Let's assume, one more time, the object is deallocted right after, this will cause crash and there is nothing i can do to stop it that comes right to my mind.
I agree, this is a lot of assumptions but i'm curious to know if someone already had this issue and how he solved it.
Thanks for reading and I hope I was clear ! :)

Yo have to always invalidate your timer before releasing it. If the timer is a part of a view controller I am always invalidating it in viewWillDisappear. For me is so odd that NSTimers retains theirs owners. I think the best way is to create - (void)cleanUp method, that will invalidate the timer and warn the user of the class to ALWAYS use cleanUp before releasing. If somebody knows the better way I will be glad.

As long as you're not using a repeating timer, why not use dispatch_after? You'll save yourself the headache and overhead of the NSTimer object. And if you just stick with GCD you can avoid the call to detachNewThreadSelector: as well.

NSRunloop will retain the timer for you, which means you don't have retain/release it at all

Since I'm making a library, i can't assume that the user will call a cleanUp function anytime.
So to solve my problem, I added a new "layer" : a new class that will do the thread and timer part so that I am the user of this class and I know I have to cleanUp !

I first resorted to using a cleanUp kind of function too.
I also have an object A owning object B that contains the timer - which retains object B. When object A is deallocated it releases object B, but B lives on because the timer retained it. Then in the method fired but the timer, I'm using a delegate relationship to call back to object A → hard crash. This is where object A needs to "cleanUp" B's timer. This could lead to other problems though if other objects also rely on object B. And also class A shouldn't and might not know the implementation secrets of class B. :-P I guess a good solution could be to unset that delegate relationship before releasing is done in A's dealloc because you don't know if B will die even if you released it - someone else might have retained it (LIKE THAT D*** NSTIMER)... :-P

Related

Is object that calls performSelector:withObject:afterDelay get retained by the NSRunLoop?

I have a certain object that perform a "Refresh" every X seconds. ("The Updater")
The way I'm doing this repetitive update is by calling performSelector:withObject:afterDelay and in my selector I'm re-scheduling as necessary.
Of course I have a method to stop these invokations by calling cancelPreviousPerformRequests.
The problem is that this "Updater" is never being deallocated.
There is only one other object that retaining the Updater (AFAIK), and the retaining object is being deallocated and calls [self setUpdater:nil];
I'm suspecting that this is have something to do with the performSelector:withObject:afterDelay method, but I couldn't find any reference to that question in the documentation.
Can anyone confirm or dismiss it?
Thanks!
APPENDIX
This are the scheduling methods:
-(void) scheduleProgressUpdate
{
[self stopProgressUpdates]; // To prevent double scheduling
[self performSelector:#selector(updateProgress)
withObject:nil
afterDelay:1.0];
}
-(void) updateProgress
{
// Perform update..
[self scheduleProgressUpdate];
}
-(void) stopProgressUpdates
{
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:#selector(updateProgress)
object:nil];
}
As far as I know the performSelector method retain its receiver and arguments.

dealloc called on same thread as running method causes crash by race condition, is that expected?

I have an object called MadsAdViewController that requests ads asynchronously, and is called back on the method didReceiveResponse. In an app with a lot of memory usage the dealloc method is called really fast, and sometimes even when the didReceiveResponse method is still running. This causes crashes, as the result of what I would call a race condition. As the output shows, both didReceiveResponse and dealloc are called on the main thread.
Why isn't the dealloc waiting for the method to finish? And why does the #synchronized block not work? And how can I fix this?
-(void)didReceiveResponse:(MadsAdResponse*) inAdResponse {
NSLog(#"didReceiveResponse: main thread? = %i, address = %p", [NSThread isMainThread], self);
#synchronized (self) {
//... (lots of stuff that takes a while)
[self logEvent:logAction eventName:EVENT_INIT action:ACTION_VIEW extra:nil];
}
NSLog(#"done with didReceiveResponse response")
}
- (void)dealloc {
#synchronized (self) {
NSLog(#"in sync block in dealloc of object %p", self);
//lots of releases
}
[super dealloc]
}
and this is the output:
didReceiveResponse: main thread? = 1, address = 0x139d50b0
in sync block in dealloc of object 0x139d50b0
and then the app crashes:
*** -[[MadsAdViewController logEvent:eventName:action:extra:]: message sent to deallocated instance 0x139d50b0
OK, turned to be a nice interaction between blocks and this piece of code listed above.
For context, our library was used by an external party in a way that we would not ahem recommend.
This is what happened around it:
XXXMadsAdViewController *adViewController = [[[XXXMadsAdViewController alloc]init]autorelease];
self.adViewController = adViewController;
[self.adViewController loadAdWithCompletionHandler:^(BOOL success) {
//stuff
}];
XXXMadsAdViewController both extended MadsAdViewController as that it was the delegate to receive the method call didReceivePartialAd. [self.delegate didReceivePartialAd] is called in the method didReceiveResponse that I didn't include in the original question and that was called before [self logEvent];
Now, sometimes self.adViewController was already released, but this block was still waiting for the callback. On callback on didReceivePartialAd, the block was processed, self.adViewController released again and the app crashed.
I fixed the problem by making didReceivePartialAd the last statement of the method didReceiveResponse.
Thanks guys, without your pointers I would still think it was a race condition!

How to self destruct an object in objective-c?

My object finish their job. He have the control of work flow, but now it have to call a function in the object that create it and have to be released. The problem is like this:
AnObject *object;
- (void)function
{
object = [[AnObject alloc] init];
[object doYourJob];
//The program continue to run next line, it don't stop here. So, I can't send a [object release] here
}
- (void)callThisWhenFinish
{
//do something
//can't call [object release] because it is in the stack and run a line of a released object
}
So, how can I release the memory of object, I try this:
Send a [object release] in callThisWhenFinish or in function. - Fail! The program still have to run some lines of AnObject.
Use delegate way to run callThisWhenFinish. - Fail! I cant release AnObject because it try to run the next line when the function callThisWhenfinish finish.
Use a NSNotificationCenter to post a notification to callThisWhenFinish. Fail! When I post notification it immediate call the callThisWhenFinish and AnObject can run the next line and it is release, causing a crash.
Use the same NSNotificationCenter with a delay on it. How safe is this way? How can I know that AnObject will not be called again?
Use the [object autorelease]. This work, but I don't know when it is done. My AnObject use a lot of RAM and have to be free as fast as possible.
Any other idea?
If your object is doing background work, it is a good idea to have it retain itself during that time. That way, you don't have to worry about it being deallocated until it is done. Your function method can safely release it after starting the action, but it won't be deallocated until it is ready to be.
AnObject *object;
- (void)function {
object = [[AnObject alloc] init];
[object doYourJob];
[object release];
}
- (void)callThisWhenFinish {
//do something
}
In AnObject:
- (void)doYourJob {
[self retain];
// enter background and call backgroundMethod
}
- (void)backgroundMethod {
// This is the method which doYourJob calls in the background to do the work
// Do some work
[delegate callThisWhenFinish];
// do whatever else needs to be done
[self release];
}
If you can't make it an ivar, why not something like this:
- (void)callThisWhenFinishAndRelease:(id)obj
Then you have a pointer to it.

Reusing NSObjects by Overriding release in Obj-C

I am implementing an object reuse scheme using a singleton class.
What I do basically is:
MyClass* obj = [[MyClassBank sharedBank] getReusableItem];
The bank is just an NSMutableSet tweaked for optimum reusability. When I was happily implementing this Singleton, I had in mind that I will just do the following when I am done with "obj":
[[MyClassBank sharedBank] doneWithItem:obj];
Currently, My code would work if I where to use it this way, but I later realized that I sometimes add "obj" to an "NSCollection", and sometimes I call:
[theCollection removeAllObjects];
At first I thought about making my own class that is composed of a collection, then I would iterate the objects within the collection and call:
[[MyClassBank sharedBank] doneWithItem:obj];
But, that's too much of a hassle, isn't?
A neat idea (I think) popped into my mind, which is to override: -(oneway void)release;, so, I immediately jumped to Apple's documentation, but got stuck with the following:
You would only implement this method to define your own reference-counting scheme. Such implementations should not invoke the inherited method; that is, they should not include a release message to super.
Ao, I was reluctant to do that idea .. basically:
-(oneway void)release{
if ([self retainCount] == 1) {
//This will increment retain count by adding self to the collection.
[[MyClassBank sharedBank] doneWithItem:self];
}
[super release];
}
Is it safe to do that?
PS: Sorry for the long post, I want the whole idea to be clear..
EDIT:
How about overriding alloc alltogther and adding [[MyClassBank sharedBank] getReusableItem]; there?
Suggested method:
You're playing with the reference counting system. 99.9999999999999999% of the time this is a bad idea. I would highly recommend going with a different mechanism. Perhaps these objects could implement their own reference count that's independent of the retainCount? Then you could use that referenceCount to actually control when an object is ready to be re-used or not.
Not suggested method:
If, for some weird reason, you can't do that, then you could do the following thing that is still a bad idea and that i don't recommend you actually use:
You can override dealloc:
- (void)dealloc {
[ivar release], ivar = nil;
[anotherIvar release], anotherIvar = nil;
somePrimitive = 0;
// do not call [super dealloc]
}
- (void)_reallyDealloc {
[self dealloc]; // clean up any ivars declared at this level
[super dealloc]; // then continue on up the chain
}
Basically, the dealloc method would be the point at which the object is ready for re-use. When you're totally done with the object and finally want it to go away, you can use the _reallyDealloc method to continue on up the chain, eventually resulting in the object getting freed.
PLEASE don't do this. With things like Automatic Reference Counting, this is going to introduce you into a world of hurt and really bizarre debugging scenarios. A lot of the tools and classes and stuff depend on the reference counting mechanism to be working without alteration, so screwing around with it is usually not a Good Idea™.
For ppl who find this approach interesting/useful, Here is a cleaner way than calling [super dealloc]; directly (which is definitely bad)
//BAD!
//-(void)dealloc{
// for some reason, the retainCount at this point == 1
// if (![[BankStep sharedBank] purgeFlag]) {
// [self resetObject];
// [[BankStep sharedBank] doneWithItem:self];
// } else {
// [children release];
// [super dealloc];
// }
//}
by calling [[Bank sharedBank] purgeBank]; , set the flag to true, then remove all objects from the NSSet.
Adapted solution:
#Joe Osborn idea of using categories to implement a returnToBank Method!

Running and managing NSTimer in different NSThread/NSRunLoop

I'm writing a Cocoa application, with a GUI designed in Interface Builder. I need to schedule background activity (at regular intervals) without blocking the UI, so I run it in a separate thread, like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[self performSelectorInBackground:#selector(schedule) withObject:nil];
}
- (void) schedule {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
timer = [[NSTimer scheduledTimerWithTimeInterval:FEED_UPDATE_INTERVAL
target:activityObj
selector:#selector(run:)
userInfo:nil
repeats:YES]
retain];
[runLoop run];
[pool release];
}
I retain the timer, so I can easily invalidate and reschedule.
Problem: I must also fire the run: method in response to GUI events, so it is synchronous (i.e. a "perform activity" button). Like this:
[timer fire];
I could do this with performSelectorInBackground too, and of course it doesn't block the UI. But this synchronous firings run in another runloop! So I have no guarantee that they won't overlap. How can I queue all of my firings on the same runloop?
[timer setFireDate:[NSDate distantPast]];
I obtained the desired effect by adjusting the next fire date to be ASAP, by passing a past date to setFireDate.
You can use a classic solution for concurrency: semaphore. In your case, the easiest way is to use #synchronized directive. Surround the entire body (or at least, the sensitive part) of run: method with #synchronized. For the synchronization object I suggest you to use a specific ivar or static variable instead of the activityObj's class in order to avoid deadlocks.
-(void)run:(id)param {
// do thread-safe things here
#synchronized(syncObj) {
// put your critical section here
}
// do more thread-safe things here
}
Code in critical section won't overlap.
you should schedule NSTimer on mainThread (and fire the timer to perform the selector --- the selector can be execute on background thread, thus do not block UI), rather than schedule NSTimer on a background thread via GCD, because NSTimer will be added to a NSRunLoop, and every NSRunLoop is associated with a NSTread. So when using GCD, use dispatch_after instead of NSTimer to delay the things to happen.