一个不用担心循环引用的Timer

2018-09-13  本文已影响0人  22点的夜生活

为什么要封装一个Timer


为什么选择GCD Timer

Timer

CADisplayLink

GCD Timer


代码

执行方法
   /**
     * startTime: 开始时间, 默认立即开始
     * interval: 间隔时间, 默认1s
     * isRepeats: 是否重复执行, 默认true
     * isAsync: 是否异步, 默认false
     * task: 执行任务
     */
class func execTask(startTime: TimeInterval = 0, interval: TimeInterval = 1, isRepeats: Bool = true, isAsync: Bool = false, task: @escaping ((_ duration: Int) -> Void)) -> String? {
        if (interval <= 0 && isRepeats) || startTime < 0 {
            return nil
        }

        let queue = isAsync ? DispatchQueue(label: "GCDTimer") : DispatchQueue.main
        let timer = DispatchSource.makeTimerSource(flags: [], queue: queue)
        timer.schedule(deadline: .now() + startTime, repeating: 1.0, leeway: .milliseconds(0))

        semphore.wait()
        let name = "\(GCDTimer.timers.count)"
        timers[name] = timer
        timersState[name] = GCDTimerState.running
        durations[name] = 0
        fireTimes[name] = Date().timeIntervalSince1970
        semphore.signal()

        timer.setEventHandler {
            var lastTotalTime = durations[name] ?? 0
            let fireTime = fireTimes[name] ?? 0
            lastTotalTime = lastTotalTime + Date().timeIntervalSince1970 - fireTime
            task(lround(lastTotalTime))
            if !isRepeats {
                self.cancelTask(task: name)
            }
        }
        timer.activate()
        return name
    }

执行方法会返回一个任务字符串, 用于外界直接取消、暂停等操作

// 使用默认值
task1 = GCDTimer.execTask(task: { (totalTimer) in
          print("定时器运行有效时间(暂停时间不会计入): \(totalTimer)")
 })

task2 = GCDTimer.execTask(startTime: 1, interval: 2, isRepeats: true, isAsync: false) { (_ ) in
          print("1s后开始, 定时器间隔2s, 允许重复执行, 不开启子线程")
 }
取消定时器
class func cancelTask(task: String?) {
        guard let _task = task else {
            return
        }
        semphore.wait()
        if timersState[_task] == .suspend {
            resumeTask(task: _task)
        }
        getTimer(task: _task)?.cancel()

        if let state = timersState.removeValue(forKey: _task) {
            print("The value \(state) was removed.")
        }

        if let timer = timers.removeValue(forKey: _task) {
            print("The value \(timer) was removed.")
        }

        if let fireTime = fireTimes.removeValue(forKey: _task) {
            print("The value \(fireTime) was removed.")
        }

        if let duration = durations.removeValue(forKey: _task) {
            print("The value \(duration) was removed.")
        }

        semphore.signal()
    }

将开启定时器时反的task1/task2传入即可

GCDTimer.cancelTask(task: task1)
暂停
class func suspendTask(task: String?) {
        guard let _task = task else {
            return
        }

        if timersState.keys.contains(_task) {
            timersState[_task] = .suspend
            getTimer(task: _task)?.suspend()

            var lastTotalTime = durations[_task] ?? 0
            let fireTime = fireTimes[_task] ?? 0
            lastTotalTime = lastTotalTime + Date().timeIntervalSince1970 - fireTime
            durations[_task] = lastTotalTime
        }
    }

调用方式同取消定时器

恢复定时器
class func resumeTask(task: String?) {
        guard let _task = task else {
            return
        }

        if timersState.keys.contains(_task) && timersState[_task] != .running {
            fireTimes[_task] = Date().timeIntervalSince1970
            getTimer(task: task)?.resume()
            timersState[_task] = .running
        }
    }

GCD Timer的resume与suspend是成对出现的, 所以不能重复resume

GitHub地址

https://github.com/zhangyadong1122/GCDTimer

上一篇下一篇

猜你喜欢

热点阅读