I'm using an NSTimer
like this:
timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target:self selector:@selector(tick) userInfo:nil repeats:YES];
Of course, NSTimer
retains the target which creates a retain cycle. Furthermore, self
isn't a UIViewController so I don't have anything like viewDidUnload
where I can invalidate the timer to break the cycle. So I'm wondering if I could use a weak reference instead:
__weak id weakSelf = self;
timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target:weakSelf selector:@selector(tick) userInfo:nil repeats:YES];
I've heard that the timer must be invalidated (i guess to release it from the run loop). But we could do that in our dealloc, right?
- (void) dealloc {
[timer invalidate];
}
Is this a viable option? I've seen a lot of ways that people deal with this issue, but I haven't seen this.
With theory and practice.Tommy's solution is not work.
Theoretically,__weak instance is as the parameter,In the implementation of
target will be retained still.
You can implement a proxy ,which hold the weak reference and forward selector calling to self , and then pass the proxy as the target. Such as YYWeakProxy.
Swift 4 version. Invalidate must be called before the dealloc.
Usage
If you are not that concerned about the millisecond accuracy of the timer events, you could use dispatch_after & __weak instead of NSTimer to do this. Here's the code pattern:
No NSTimer @property, no invalidate/runloop stuff and no proxy object, just a simple clean method.
The downside of this approach is that (unlike
NSTimer
) the execution time of the block (containing[weakSelf doSomethingRepeatedly];
) will impact scheduling of the events.Swift 3
App target < iOS 10:
Custom WeakTimer (GitHubGist) implementation:
Usage:
timer
is instance of standard classTimer
, so you can use all available methods (e.g.invalidate
,fire
,isValid
,fireDate
and etc).timer
instance will be deallocated whenself
is deallocated or when timer's job is done (e.g.repeats == false
).App target >= iOS 10:
Standard Timer implementation:
Usage:
iOS 10 and macOS 10.12 "Sierra" introduced a new method,
+scheduledTimerWithTimeInterval:repeats:block:
, so you could captureself
weakly simply as:Equivalence in Swift 3:
If you still need to target iOS 9 or below (which you should at this moment), this method cannot be used, so you would still need to use code in the other answers.
In Swift I've defined a
WeakTimer
helper class:And then you can use it like such:
The returned
NSTimer
has a weak reference toself
, so you can call itsinvalidate
method indeinit
.