Search code examples
iosobjective-cblocknstimer

Write equivalent code for in iOS 9


In my app I have a following piece of code:

__weak __typeof(self)weakSelf = self;

_pingTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
                                             repeats:YES
                                               block:^(NSTimer * _Nonnull timer)

{
    __strong __typeof(weakSelf)strongSelf = weakSelf;
    [strongSelf pingWithBlock:nil];
}];

this works perfectly in iOS 10+, but I need the app to support iOS 9 as well. So I needed to provide a method that would work for both.

I tried this:

__weak __typeof(self)weakSelf = self;

_pingTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
                                             target:weakSelf
                                           selector:@selector(pingWithBlock:)
                                           userInfo:nil
                                            repeats:YES];

pingWithBlock method is defined in the same class, it's an instance method.

But this doesn't seem to work, meaning I get a bad memory access crash.

If anyone has any suggestions it will be highly appreciated.

EDIT: thanks to @dgatwood explanations code below fixes the issue

- (void)autoPing
{
   _pingTimer = [NSTimer scheduledTimerWithTimeInterval:self.autoCheckInterval
                                             target:self
                                           selector:@selector(pingWithBlock)
                                           userInfo:nil
                                            repeats:YES];
}

-(void)pingWithBlock
{
    [self pingWithBlock:nil];
}

Solution

  • This is kind of odd. NSTimer retains its target. Maybe that doesn't happen in this case because of the __weak, but I thought it did anyway. *shrugs*

    Either way, this sounds like a multithreading race condition:

    • Your timer isn't retaining the object, so it could go away at any time.
    • Something else is retaining the object.
    • The timer is scheduled in the runloop of the thread that was running when the timer was constructed.
    • That something else disposes of the reference to the object in another thread.
    • The timer fires in the first thread and the zeroing weak reference hasn't zeroed because the object is still halfway through destroying itself.
    • A crash occurs.

    The best fix is to let the timer retain the target object (by removing all the weakSelf stuff). If the timer is a repeating timer, provide a method to allow the code that disposes of the enclosing object to cancel that timer, and be careful to always call it.