会不会产生问题分两种情况:
如果NSTimer在生成时repeats参数被设为NO,即一次性的定时器,那么不会有问题,定时器在执行完任务后会自动失效,从而打破循环引用。
如果NSTimer在生成时repeats参数被设为YES,那么NSTimer会一直保留目标对象,直到自身失效。因此retain该类型的NSTimer使用不当便会造成循环引用。
解决第二种情况的办法:
iOS10中,NSTimer的API中新增了一个带block参数的方法。使用该API可以解决循环引用的问题。该API的实现原理大致如下:
为NSTimer增加一个Category,具体代码如下
//.h文件
#import <Foundation/Foundation.h>
typedef void(^TimerFireBlock)(NSTimer *timer);
@interface NSTimer (CZAddition)
// 只执行一次
+ (instancetype)scheduledTimerWithTimeInterval: (NSTimeInterval)inTimeInterval
firing: (TimerFireBlock)fireBlock;
+ (instancetype)scheduledTimerWithTimeInterval:(NSTimeInterval)inTimeInterval
repeating:(BOOL)repeat
firing:(TimerFireBlock)fireBlock;
//.m文件
#import "NSTimer+CZAddition.h"
@implementation NSTimer (CZAddition)
+ (void)excuteTimerBlock:(NSTimer *)timer {
TimerFireBlock block = [timer userInfo];
block(timer);
}
+ (instancetype)scheduledTimerWithTimeInterval:(NSTimeInterval)inTimeInterval
firing:(TimerFireBlock)fireBlock {
return [self scheduledTimerWithTimeInterval:inTimeInterval
repeating:NO
firing:fireBlock];
}
+ (instancetype)scheduledTimerWithTimeInterval:(NSTimeInterval)inTimeInterval
repeating:(BOOL)repeat
firing:(TimerFireBlock)fireBlock {
return [self scheduledTimerWithTimeInterval:inTimeInterval
target:self
selector:@selector(excuteTimerBlock:)
userInfo:[fireBlock copy]
repeats:repeat];
}
@end
在控制器中以这种方式使用NSTimer时,一定要在dealloc方法中调用[self.timer invalidate]
,使得RunLoop释放对timer的强引用。
上述方法之所以能够避免内存泄漏的问题的关键在于把保留对象(即target)转移到了定时器类对象身上,这样就避免了实例对象被保留。因为循环引用指的是实例对象间的引用关系。类对象在App杀死时才会释放,在实际开发中几乎不需要关注类对象的内存管理。
参考文章:
iOS | 小心NSTimer中的内存泄漏