Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between weak references in a block and a NSTimer

As we know we need to use a weak reference inside a block to break the retain cycle, like so:

__weak id weakSelf = self;
[self doSomethingWithABlock:^() {
    [weakSelf doAnotherThing];
}]

However weak references can not break the retain cycle caused by a NSTimer.

__weak id weakSelf = self;
timer = [NSTimer scheduledTimerWithTimeInterval:30.0f 
                                         target:weakSelf
                                       selector:@selector(tick) 
                                       userInfo:nil
                                        repeats:YES]; // No luck

What's the difference? How can the timer still retain the target?

like image 565
dark_chenshifei Avatar asked Jul 06 '17 03:07

dark_chenshifei


1 Answers

The whole problem with the selector-based NSTimer technique is that it establishes a strong reference to the object you pass to it. So whether the variable you used to hold the reference to the target you passed to scheduledTimerWithTimeInterval was, itself, strong or weak, is immaterial. Assuming the target reference wasn't nil by the time the selector-based scheduled timer was scheduled, NSTimer will establish its own strong reference. The "weak" vs "strong" nature of the references in calling code only dictates where ARC will place its own memory management calls in the caller's code, but the target is just a simple pointer, and none of this weak vs strong information is conveyed to NSTimer. The selector-based NSTimer will establish its own strong reference that is not resolved until the timer is invalidated.

This is why, when we want to invalidate a timer built via the selector-based method, we have to it in viewDidDisappear or the like, rather than dealloc.

Note, scheduledTimerWithTimeInterval now has a block-based variation for iOS 10 and later, so you can enjoy the weak reference pattern of blocks if you don't have to support earlier iOS versions:

typeof(self) __weak weakSelf = self;
[NSTimer scheduledTimerWithTimeInterval:30 repeats:true block:^(NSTimer * _Nonnull timer) {
    // use weakSelf here
}];
like image 130
Rob Avatar answered Oct 22 '22 15:10

Rob