使用 GCD 替代 NSTimer
使用 NSTimer 或者 performSelector 进行延迟操作有以下需要注意的:
1. 必须保证有一个活跃的runloop。
performSelector和scheduledTimerWithTimeInterval方法都是基于runloop的。当一个应用启动时,系统会开启一个主线程,并且把主线程的runloop激活,也就是run起来,并且主线程的runloop是不会停止的。所以,当这两个方法在主线程可以被正常调用。但情况往往不是这样的。实际编码中,我们更多的逻辑是放在子线程中执行的。而子线程的runloop是默认关闭的。这时如果不手动激活runloop,performSelector和scheduledTimerWithTimeInterval的调用将是无效的。
2.NSTimer的创建与撤销必须在同一个线程操作、performSelector的创建与撤销必须在同一个线程操作。
3.内存管理有潜在泄露的风险
scheduledTimerWithTimeInterval方法将target设为A对象时,A对象会被这个timer所持有,也就是会被retain一次,timer会被当前的runloop所持有。performSelector:withObject:afterDelay:方法实际上是在当前线程的runloop里帮你创建的一个timer去执行任务,所以和scheduledTimerWithTimeInterval方法一样会retain其调用对象。
当一个timer被schedule的时候,timer会持有target对象,NSRunLoop对象会持有timer。当invalidate被调用时,NSRunLoop对象会释放对timer的持有,timer会释放对target的持有。除此之外,我们没有途径可以释放timer对target的持有。所以解决内存泄露就必须撤销timer,若不撤销,target对象将永远无法释放。
若使用dispatch_after,系统会帮我们处理线程级的逻辑,这样也我们更易于享受系统对线程所做的优化。除此之外,我们不用关心runloop的问题。并且调用的对象也不会被强行持有,这样上述的内存问题也不复存在。当然,需要注意block会持有其传入的对象,但这可以通过weakself解决。所以在这种延迟操作方案中,使用dispatch_after更佳。
dispatch_after有个致命的弱点:dispatch_after一旦执行后,就不能撤销了。而performSelector可以使用cancelPreviousPerformRequestsWithTarget方法撤销,NSTimer也可以调用invalidate进行撤销。(注意:撤销任务与创建timer任务必须在同一个线程,即同一个runloop)
以下:使用 GCD 替代 NSTimer
OC 代码如下
//创建一个 time 并放到队列中
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
//需要强引用否则 time会销毁,无法继续执行
self.timer = timer;
//首次执行时间 间隔时间 时间精度
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"----");
});
//激活 timer
dispatch_resume(timer);
SWIFT 给我搞懵逼了 如果你知道请告诉我