Swift4: Timer的缺点与GCD定时器的简单封装

Timer的缺点

  • 缺点1
    Timer的创建与撤销必须在同一个线程操作,在多线程环境下使用不便.
  • 缺点2
    使用时必须保证有一个活跃的runloop.
    然而主线程的runloop是默认开启的,子线程的runloop却是默认不开启的,当在子线程中使用Timer的时候还需要先激活runloop,否则Timer是不会起效的.
  • 缺点3
    内存泄漏问题.
    在控制器中使用Timer的时候需要控制器对Timer进行强引用,然而Timer还会对控制器进行强引用,造成循环引用最终控制器无法释放导致内存泄漏.

控制器 强引用> Timer 强引用> 控制器

这个问题解决的办法有两种:
1.在一个合适的地方对Timer进行销毁解除循环引用.
但是这样其实做的是MRC的事情,在ARC环境下显得格格不入.

  1. 使用block来执行操作
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { (timer) in
             //执行的代码
}

这样做也存在问题,这个API是iOS10以后才推出的,当前iOS版本为iOS11最少也要兼容到iOS9,这个API现在用的话还是早了些.

Timer缺点的解决

对以上问题的解决答案其实就是改用GCD定时器.GCD定时器的优点是更加精准也不存在上述的问题.

GCD定时器的简单封装

typealias ActionBlock = () -> ()

class MCGCDTimer {
    //单例
    static let shared = MCGCDTimer()
    
    lazy var timerContainer = [String: DispatchSourceTimer]()
    
    /// GCD定时器
    ///
    /// - Parameters:
    ///   - name: 定时器名字
    ///   - timeInterval: 时间间隔
    ///   - queue: 队列
    ///   - repeats: 是否重复
    ///   - action: 执行任务的闭包
    func scheduledDispatchTimer(WithTimerName name: String?, timeInterval: Double, queue: DispatchQueue, repeats: Bool, action: @escaping ActionBlock) {
        
        if name == nil {
            return
        }
        
        var timer = timerContainer[name!]
        if timer == nil {
            timer = DispatchSource.makeTimerSource(flags: [], queue: queue)
            timer?.resume()
            timerContainer[name!] = timer
        }
        //精度0.1秒
        timer?.schedule(deadline: .now(), repeating: timeInterval, leeway: DispatchTimeInterval.milliseconds(100))
        timer?.setEventHandler(handler: { [weak self] in
            action()
            if repeats == false {
                self?.cancleTimer(WithTimerName: name)
            }
        })
    }
    
    /// 取消定时器
    ///
    /// - Parameter name: 定时器名字
    func cancleTimer(WithTimerName name: String?) {
        let timer = timerContainer[name!]
        if timer == nil {
            return
        }
        timerContainer.removeValue(forKey: name!)
        timer?.cancel()
    }
    
    
    /// 检查定时器是否已存在
    ///
    /// - Parameter name: 定时器名字
    /// - Returns: 是否已经存在定时器
    func isExistTimer(WithTimerName name: String?) -> Bool {
        if timerContainer[name!] != nil {
            return true
        }
        return false
    }
    
}

使用

  • 开始定时器

      MCGCDTimer.shared.scheduledDispatchTimer(WithTimerName: "GCDTimer", timeInterval: 1, queue: .main, repeats: true) {
          //需要执行的代码
      }
    
  • 取消定时器

      MCGCDTimer.shared.cancleTimer(WithTimerName: "GCDTimer")
    
  • 检查定时器是否存在

      MCGCDTimer.shared.isExistTimer(WithTimerName: "GCDTimer")
    

最后附上demo链接:

https://github.com/drenhart/MCGCDTimer

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容