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?
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
}];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With