Crash when checking if NSTimer isValid?

1.1k Views Asked by At

I am getting this exception in the console:

Error:

2015-06-25 23:12:01.841 Copyfeed for Mac[9512:584232] -[_NSViewLayoutAux invalidate]: unrecognized selector sent to instance 0x6000001657c0

when checking if my timers are valid/and when invalidating them.

if ([_staticTimer isValid]) {
    [_staticTimer invalidate];
    _selectionTimer = 
            [NSTimer scheduledTimerWithTimeInterval:2 target:self 
            selector:@selector(hideHUD) userInfo:nil repeats:NO];
}

if ([_selectionTimer isValid]) {
    [_selectionTimer invalidate];
    _selectionTimer = 
           [NSTimer scheduledTimerWithTimeInterval:2 target:self 
           selector:@selector(hideHUD) userInfo:nil repeats:NO];
}

This is my new code:

 if (_selectionTimer != nil) {
        [_selectionTimer invalidate];
        _selectionTimer = nil;
        _selectionTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(hideHUD) userInfo:nil repeats:NO];
    }
    
    if (_staticTimer != nil) {
        [_staticTimer invalidate];
        _staticTimer = nil;
        _selectionTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(hideHUD) userInfo:nil repeats:NO];
    }


@property  (strong )NSTimer *staticTimer;
    @property  (strong )NSTimer *selectionTimer;

Now getting this error when I debug with zombie objects on.

2015-06-26 00:39:45.523 Copyfeed for Mac[11191:824502] *** -[CFRunLoopTimer release]: message sent to deallocated instance 0x608000175e40

3

There are 3 best solutions below

0
On BEST ANSWER

There are a couple of potential issues here.

if (_staticTimer != nil) {
    [_staticTimer invalidate];
    _staticTimer = nil;
    _selectionTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(hideHUD) userInfo:nil repeats:NO];
}

What you are doing here is overriding the selectionTimer even though it might still contain a timer that is still scheduled in the run loop. So if you reset the property here, you should also make sure to call [_selectionTimer invalidate] before doing so.

Depending on what you are doing when the timer is firing, this could explain the crash on CFRunLoopTimer.

A general advice that turned out to be very helpful for me when working with NSTimer: I would recommend declaring all the properties that hold a scheduled timer as weak as they are retained by the run loop anyway. This way, you don't need to explicitly set them to nil after invalidating them but instead you can simply call invalidate every time you want to get rid of it and it will also automatically become nil once it has fired by the run loop, releasing all the data it might hold on to. Note that this would still require you to call invalidate in case you want to cancel a timer or before replacing one, but you no longer need to set it to nil after doing so.

0
On

From the documentation:

Because the run loop maintains the timer, from the perspective of memory management there's typically no need to keep a reference to a timer after you’ve scheduled it. Since the timer is passed as an argument when you specify its method as a selector, you can invalidate a repeating timer when appropriate within that method. In many situations, however, you also want the option of invalidating the timer—perhaps even before it starts. In this case, you do need to keep a reference to the timer, so that you can send it an invalidate message whenever appropriate. If you create an unscheduled timer (see “Unscheduled Timers”), then you must maintain a strong reference to the timer (in a reference-counted environment, you retain it) so that it is not deallocated before you use it.

So you should make the timer weak instead of strong

2
On

After you invalidate it, you should set an NSTimer object to nil. invalidate method also does a release. If you didn't do that, calling a method on it like isValid could cause your crash.

if (_selectionTimer != nil) {
    [_selectionTimer invalidate];
    _selectionTimer = nil;
    // do something
}

Check here

if (_staticTimer != nil) {
        [_staticTimer invalidate];
        _staticTimer = nil;
        //_selectionTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(hideHUD) userInfo:nil repeats:NO];
        _staticTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(hideHUD) userInfo:nil repeats:NO];
    }