这里介绍一种更加轻量级的 Timer 实现, 适用于各种需要 Timer 的场景.

DispatchTimer 的使用

在这里封装一个用于后台线程中的计时器,如下所示:

swift
public protocol BackgroundTimer {
    func resume()
    func suspend()
}

public class BackgroundTimerMaker {
    public static func makeTimer(adding: TimeInterval, task: (()->())?) -> BackgroundTimer {
        return BackgroundTimerImp(deadline: .now() + adding, repeating: .never, task: task)
    }
    
    public static func makeTimer(adding: TimeInterval, repeatInterval: Int, task: (()->())?) -> BackgroundTimer {
        return BackgroundTimerImp(deadline: .now() + adding, repeating: .seconds(repeatInterval), task: task)
    }
    
    public static func makeTimer(repeatInterval: Int, task: (()->())?) -> BackgroundTimer {
        return BackgroundTimerImp(deadline: .now(), repeating: .seconds(repeatInterval), task: task)
    }
}

class BackgroundTimerImp: BackgroundTimer {
    
    private var task: (()->())?
    
    private let _timer: DispatchSourceTimer
    
    private let _lock = NSLock()
    
    private enum State {
        case suspended
        case resumed
    }
    
    private var state: State = .suspended
    
    init(deadline: DispatchTime, repeating: DispatchTimeInterval = .never,
         leeway: DispatchTimeInterval = .milliseconds(100), task: (()->())?) {
        self.task = task
        _timer = DispatchSource.makeTimerSource()
        _timer.schedule(deadline: deadline,
                        repeating: repeating,
                        leeway: leeway)
        _timer.setEventHandler(handler: {
            task?()
        })
    }
    
    func resume() {
        guard state != .resumed else { return }
        _lock.lock()
        guard state != .resumed else { return }
        state = .resumed
        _timer.resume()
        defer {
            _lock.unlock()
        }
    }
    
    func suspend() {
        guard state != .suspended else { return }
        _lock.lock()
        guard state != .suspended else { return }
        state = .suspended
        _timer.suspend()
        defer {
            _lock.unlock()
        }
    }
    
    deinit {
        // 注意: 这里必须这样写, 否则在释放 timer 的时候会发生崩溃

        _timer.setEventHandler {}
        _timer.cancel()
        resume()
        task = nil
    }
}