首页 > 代码库 > 初识iOS NSTimer 循环引用不释放问题
初识iOS NSTimer 循环引用不释放问题
原文转自 :http://www.codecate.com/code/?p=77
最近开发遇到NSTimer Target 造成循环引用问题,不释放,以下是解决方案。
stackoverflow上的一个解决方案
http://stackoverflow.com/questions/16821736/weak-reference-to-nstimer-target-to-prevent-retain-cycle
原文如下
Weak Reference to NSTimer Target To Prevent Retain Cycle
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.
解决方案
The proposed code:
__weak id weakSelf = self; timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target:weakSelf selector:@selector(tick) userInfo:nil repeats:YES];
has the effect that (i) a weak reference is made to self; (ii) that weak reference is read in order to provide a pointer to NSTimer. It won’t have the effect of creating an NSTimer with a weak reference. The only difference between that code and using a __strong reference is that if self is deallocated in between the two lines given then you’ll pass nil to the timer.
The best thing you can do is create a proxy object. Something like:
[...] @implementation BTWeakTimerTarget { __weak target; SEL selector; }
[...]
- (void)timerDidFire:(NSTimer *)timer { if(target) { [target performSelector:selector withObject:timer]; } else { [timer invalidate]; } } @end
Then you‘d do something like:
BTWeakTimerTarget *target = [[BTWeakTimerTarget alloc] initWithTarget:self selector:@selector(tick)]; timer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:target selector:@selector(timerDidFire:) ...];
Or even add a class method to BTWeakTimerTarget of the form +scheduledTimerWithTimeInterval:target:selector:… to create a neater form of that code. You’ll probably want to expose the real NSTimer so that you can invalidate it, otherwise the rules established will be:
the real target isn’t retained by the timer;
the timer will fire once after the real target has begun (and probably completed) deallocation, but that firing will be ignored and the timer invalidated then.
使用GCD 方式解决代码
– (void) doSomethingRepeatedly
{
// Do it once
NSLog(@”doing something …”);
// Repeat it in 2.0 seconds
__weak typeof(self) weakSelf = self;
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[weakSelf doSomethingRepeatedly];
});
}
初识iOS NSTimer 循环引用不释放问题