JKAutoReleaseTimer 1.0.3

JKAutoReleaseTimer 1.0.3

XiFengLang 维护。



  • XiFengLang

JKAutoReleaseTimer 自释放定时器(NSTimer + GCD 定时器)

  • JKNSTimerHolder 基于NSTimer包装的自释放定时器
  • JKGCDTimerHolder 基于dispatch_queue_t封装的自释放定时器

CocoaPods

source 'https://github.com/CocoaPods/Specs.git'

pod 'JKAutoReleaseTimer', '~> 1.0.0'

先思考一个问题:在一个由导航控制器 NaviVC 管理的控制器 VC 中运行一个定时器 NSTimer,target 为 VC(self),重复 20 次,假设在第 10 次的时候退出 VC(出栈),怎么在最短的时间内停止定时器并释放 VC?

在常规的 NSTimer 用法中,self 会被强引用,必须先释放_timer 才能释放 self,如果_timer没能及时释放,就会出现内存泄露,这种情况在《Effective Objective-C 2.0 编写高质量 iOS 与 OS X 代码的 52 个有效方法》的第 52 条被提到。而基于 GCD 的 dispatch_source_set_event_handler 也有类似的缺点,容易强引用外部变量,引起循环引用或者内存泄露。

    _timer = [NSTimer scheduledTimerWithTimeInterval:second
                                              target:self
                                            selector:@selector(handleTimerAction:)
                                            userInfo:nil
                                             repeats:yesOrNo];
                                             

self与timer

JKNSTimerHolder

JKNSTimerHolder 在 self 和 timer 之间增加了 "桥梁对象" 类,将 self 和 timer 解耦。timerHolder 管理timer,timer 强引用 timerHolder,两者之间存在引用环。但是 timerHolder 弱引用 self,一旦 self 被释放就会主动废除定时器以实现自释放。而外部的 self 同样可以主动控制 timerHolder,暂停或废除定时器,达到释放效果。对于前面提出的问题,在这就能迎刃而解,一旦控制器 VC 出栈,VC 没有被额外的强引用就会释放,timerHolder 也会自动废除定时器实现自释放。

d


    JKNSTimerHolder * timerHolder = [[JKNSTimerHolder alloc] init];
    
    /// 强/弱引用都有可以
    self.timerHolder = timerHolder;

	[timerHolder jk_startNSTimerWithTimeInterval:0.5
                                     repeatCount:self.repeatCount
                                   actionHandler:self
                                          action:@selector(jk_sel:)];
	
	/// 暂停
	/// self.timerHolder.suspended = YES;


	/// 废除定时器
	/// [self.timerHolder jk_cancelNSTimer];

JKNSTimerHolder 还支持 Block 块,写法如下:


 @param seconds 时间间隔
 @param repeatCount 重复次数,repeatCount == 运行总数 -1,达到重复次数后会自动停止定时器
 @param handler 回调响应者 == handle中的tempSelf
 @param handle 回调Block


    [timerHolder jk_startBlockTimerWithTimeInterval:0.5
                                        repeatCount:self.repeatCount
                                      actionHandler:self
                                             handle:^(JKNSTimerHolder * _Nonnull jkTimer, id  _Nonnull tempSelf, NSUInteger currentCount) {
        
        ///  tempSelf == 传入的actionHandler,使用tempSelf不会发生循环引用
        [(NSTimerTestVC *)tempSelf jk_sel:jkTimer];
    }];

在这里需要理解一个知识点,即 Block 对参数对象的引用,非 Block 对外部对象的引用。经测试,Block 会在执行过程中强引用参数对象,执行完毕后就会解除强引用。这个测试过程在文章Block 与 Copy中提到,尽管文章中提到的结论有误,一位大兄弟在评论中指出了错误之处,但文章中的测试代码还是很有参考价值的。上面的代码中,Block 有一个 tempSelf 参数,这个参数就是传入的 actionHandler:self,在 handleBlock 中使用 tempSelf 不会出现循环引用,但如果仍然使用 self,那就可能出现循环引用,需要将 self 进行 weak strong 转换才行。

JKGCDTimerHolder

JKGCDTimerHolder基于GCD的dispatch_source_set_event_handler实现,但其原理和用法与JKNSTimerHolder相同。


    JKGCDTimerHolder * gcdTimerHolder = [[JKGCDTimerHolder alloc] init];
    
    /// 强/弱引用都有可以
    self.gcdTimerHolder = gcdTimerHolder;
    
    [self.gcdTimerHolder jk_startGCDTimerWithTimeInterval:0.5
                                              repeatCount:self.repeatCount
                                            actionHandler:self
                                                   action:@selector(gcdTimerAction)];



	/// 废除定时器
	/// [self.gcdTimerHolder jk_cancelGCDTimer];

Block写法

    [timerHolder jk_startBlockTimerWithTimeInterval:0.5
                                        repeatCount:self.repeatCount
                                      actionHandler:self
                                             handle:^(JKNSTimerHolder * _Nonnull jkTimer, id  _Nonnull tempSelf, NSUInteger currentCount) {
        
        ///  tempSelf == 传入的actionHandler,使用tempSelf不会发生循环引用
        [(NSTimerTestVC *)tempSelf jk_sel:jkTimer];
    }];