标签:
大多数开发者可能都会这样来实现定时器。创建定时器的时候,由于目标对象是self,所以要保留此实例。然而,因为定时器是用实例变量存放的,所以实例也保留了定时器,这就造成了循环引用。除非调用stop方法,或者系统回收实例,才能打破循环引用,如果无法确保stop一定被调用,就极易造成内存泄露。
当指向XXClass实例的最后一个外部引用移走之后,该实例仍然会继续存活,因为定时器还保留着它。而定时器对象也不可能被系统释放,因为实例中还有一个强引用正在指向它。这种内存泄露是很严重的,如果定时器每次轮训都执行一些下载工作,常常会更容易导致其他内存泄露问题。
这个问题可以通过Block来解决:
@interface NSTimer (XXBlocksSupport) + (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats; @end @implementation NSTimer (XXBlocksSupport) + (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats { return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(xx_blockInvoke:) userInfo:[block copy] repeats:repeats]; } + (void)xx_blockInvoke:(NSTimer *)timer { void (^block)() = timer.userinfo; if(block) { block(); } } @end
定时器现在的target是NSTimer类对象,这是个单例,此处依然有循环引用,然后类对象无需回收,所以不用担心。
这套代码并不能解决问题,例如:
- (void)start { timer = [NSTimer xx_scheduledTimerWithTimeInterval:.5 block:^{ [self doSomething]; } repeats:YES]; }
这段代码里还是有循环引用,因为Block捕获了self变量。此处只要改用weak引用,即可打破循环引用。
- (void)start { __weak XXClass *weakSelf = self; timer = [NSTimer xx_scheduledTimerWithTimeInterval:.5 block:^{ XXClass *strongSelf = weakSelf; [strongSelf doSomething]; } repeats:YES]; }
先定义了一个弱引用,令其指向self,然后使块捕获这个引用,而不直接去捕获普通的self变量。也就是说,self不会为计时器所保留。当块开始执行时,立刻生成strong引用,以保证实例在执行期间持续存活。
采用这种写法之后,如果外界指向XXClass实例的最后一个引用将其释放,则该实例就可为系统所回收了。
标签:
原文地址:http://www.cnblogs.com/zhuyiios/p/5454933.html