NSTimer
NSTimer是Foundation框架中一种很方便很有用的对象,可以:
- 指定绝对的日期和时期,以便到时执行指定任务
- 指定执行任务的相对延迟时间
- 指定重复运行的任务
计时器要和run loop
(运行循环)相关联,run loop
到时候会触发任务。创建NSTimer时,可以将其预先安排在当前run loop
中,也可以先创建好,然后手动调用加入run loop
中,它才能正常触发任务。
系统方法:
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval
target:(id)target
selector:(SEL)aSelector
userInfo:(nullable id)userInfo
repeats:(BOOL)repeats;
直接创建个实例对象,并将其加入当前run loop
当中。由于计时器会保留目标对象target,所以反复执行任务通常会导致应用程序问题。也就是设置重复执行模式的那种计时器,很容易引入retain cycle
(保留环)。
如何打破Retain Cycle
- MSWeakTimer
- 为NSTimer添加个handlerBlock
推荐MSWeakTimer:
线程安全的Timer,不会对target进行retain操作,支持GCD Queue,NSTimer的替代品MSWeakTimer现已支持Pod,具体实现及用法请点击这里
下面主要谈谈第二种,如何自己实现来打破retain cycle
。
1.为NSTimer添加一个Category方法
NSTimer+WeakTimer.h
+ (NSTimer *)zx_scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval
repeats:(BOOL)repeats
handlerBlock:(void(^)())handler;
NSTimer+WeakTimer.m
+ (NSTimer *)zx_scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval
repeats:(BOOL)repeats
handlerBlock:(void(^)())handler
{
return [self scheduledTimerWithTimeInterval:timeInterval
target:self
selector:@selector(handlerBlockInvoke:)
userInfo:[handler copy]
repeats:repeats];
}
+ (void)handlerBlockInvoke:(NSTimer *)timer
{
void (^block)() = timer.userInfo;
if (block) {
block();
}
}
2.如何使用这个Category方法
创建一个NSTimer
- (void)startPolling
{
__weak typeof(self)weakSelf = self;
self.timer = [NSTimer zx_scheduledTimerWithTimeInterval:5.0 repeats:YES handlerBlock:^void(void){
__strong typeof(weakSelf)strongSelf = weakSelf;
[strongSelf doPolling];
}];
}
执行轮询任务slector
- (void)doPolling
{
//Todo...;
}
销毁NSTimer对象
- (void)stopPolling
{
[self.timer invalidate];
self.timer = nil;
}
- (void)dealloc
{
[self.timer invalidate];
}
计时器现在的targer是NSTimer类对象。这段代码先是定义了个弱引用,令其指向self,然后block捕获这个引用,而不直接去捕获普通的self变量,也就是说self不会为计时器所保留。当block开始执行时,立刻生成strong强引用,以保证实例在执行期间持续存活,不被释放。
采用这种写法后,外界指向NSTimer的实例最后一个引用被释放后,则创建NSTimer的实例也随之被系统回收。
Refer: Effective Objective-C 2.0 Tips52