16

RxSwift中的Timer

 4 years ago
source link: https://www.tuicool.com/articles/RJzUJji
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

我们在项目中经常会用到定时器,先来看下 swift 中使用定时器的几种方式:

Timer

//第一种写法
timer1 = Timer.init(timeInterval: 1,
                    target: self,
                    selector: #selector(timerFire),userInfo: nil,
                    repeats: true)
RunLoop.current.add(timer1, forMode: .common)

//第二种写法
timer = Timer.scheduledTimer(withTimeInterval: 1,
                            repeats: true,
                            block: { (timer) inprint("定时中。。。")
})
RunLoop.current.add(timer, forMode: .common)复制代码

这两种只是写法不同而已,第一种初始化的方式创建的 timer 需要手动添加到 runloop 中才能启动。第二种 scheduled 开头的方法,会默认把 timer 加入到当前 runloopdefault 模式下。但它们都需要改成 common 模式, runloop 才会在滚动视图时同时也响应定时器。这个 timer 的生命周期以及内存泄漏风险都需要我们自己管理。

CADisplayLink

displayLink = CADisplayLink(target: self, selector: #selector(timerFire))displayLink?.preferredFramesPerSecond = 1
displayLink?.add(to: RunLoop.current, forMode: .common)复制代码

CADisplayLinkTimer 差不多,是根据屏幕刷新频率来的,也是需要添加在 common 模式下。

DispatchSourceTimer

gcdTimer = DispatchSource.makeTimerSource()
gcdTimer?.schedule(deadline: DispatchTime.now(),
                    repeating: DispatchTimeInterval.seconds(1))
gcdTimer?.setEventHandler(handler: {print("定时中。。。")
})
gcdTimer?.resume()复制代码

CGD中的定时器在使用时,不受主线程 runloop 的影响。它会在自己所在的线程中一直执行下去,直到你 suspend 或者 cancel 掉它。而且还可以指定 handler 所执行的线程。

RxSwift的Timer

let _ = Observable.interval(RxTimeInterval.seconds(1), scheduler: MainScheduler())
    .subscribe(onNext: { (state) inprint(state)
    })
    .disposed(by: disposeBag)复制代码

首先,这个 timer 也是不用关心 runloop 方面的问题。

进去看看这个 interval 函数:

public static func interval(_ period: RxTimeInterval, 
                            scheduler: SchedulerType) -> Observable{return Timer(
        dueTime: period,
        period: period,
        scheduler: scheduler
    )
}复制代码

参数1: RxTimeInterval ,就是 DispatchTimeInterval 的别名。 public typealias RxTimeInterval = DispatchTimeInterval

参数2:调度者, MainScheduler() 的视线中能看出,我们创建的是一个主线程调度者。

public init() {
    self._mainQueue = DispatchQueue.main
    super.init(serialQueue: self._mainQueue)
}复制代码

返回值是一个初始化的 Timer 对象, timer 中保存了这些参数值,到此为止。

final private class Timer: Producer{
    fileprivate let _scheduler: SchedulerType
    fileprivate let _dueTime: RxTimeInterval
    fileprivate let _period: RxTimeInterval?

    init(dueTime: RxTimeInterval, period: RxTimeInterval?, scheduler: SchedulerType) {
        self._scheduler = scheduler
        self._dueTime = dueTime
        self._period = period
    }
}复制代码

这个 timerProducer 的子类,也是一个序列。 timer 中的泛型要求遵守 RxAbstractInteger 协议的。这个协议也是个别名, public typealias RxAbstractInteger = FixedWidthIntegerFixedWidthInteger 协议要求遵守它的实例都是用固定大小的整数类型。所以我们在创建序列的时候把泛型指定为 Int

在创建了 timer 后,我们又进行了订阅,熟悉 RxSwift核心逻辑 的话,就会清楚订阅信号后,内部会创建一个匿名的观察者,然后返回创建的销毁者中会调用 timer序列 的订阅函数,这里又根据你指定的线程在不同的分支中走了 timer序列run

func run(_ observer: Observer, 
                                cancel: Cancelable) -> (sink: Disposable, 
                                                        subscription: Disposable) {if self._period != nil {let sink = TimerSink(parent: self, observer: observer, cancel: cancel)let subscription = sink.run()return (sink: sink, subscription: subscription)
    } else {
        ......
    }
}复制代码

这里还是 RxSwift核心逻辑 熟悉的路子,创建 TimerSink 并调用 run

final private class TimerSink: Sinkwhere Observer.Element : RxAbstractInteger  {
    typealias Parent = Timerprivate let _parent: Parent
    private let _lock = RecursiveLock()

    init(parent: Parent, observer: Observer, cancel: Cancelable) {
        self._parent = parent
        super.init(observer: observer, cancel: cancel)
    }

    func run() -> Disposable {return self._parent._scheduler.schedulePeriodic(0 as Observer.Element, 
                                                        startAfter: self._parent._dueTime,  
                                                        period: self._parent._period!) 
        { state inself._lock.lock(); defer { self._lock.unlock() }
            self.forwardOn(.next(state))return state &+ 1
        }
    }
}复制代码

run 里面主要就是 schedulePeriodi ,起始时间默认为0,后面两个都是 timer序列 的间隔时间,最后还有个闭包。这个函数会在我们外面设置的那个 主线程scheduler 中调用,为了防止多线程调用导致数据错误,这里加了线程锁。 state &+ 1 也对应了 FixedWidthInteger 协议,防止溢出。继续钻进去:

MainScheduler 的父类 SerialDispatchQueueSchedulerschedulePeriodi 的实现:

public func schedulePeriodic(_ state: StateType, 
                                        startAfter: RxTimeInterval, 
                                        period: RxTimeInterval, 
                                        action: @escaping (StateType) -> StateType) -> Disposable {return self.configuration.schedulePeriodic(state, 
                                                startAfter: startAfter, 
                                                period: period, 
                                                action: action)
}复制代码

又是一个接口:

func schedulePeriodic(_ state: StateType, 
                                startAfter: RxTimeInterval, 
                                period: RxTimeInterval, 
                                action: @escaping (StateType) -> StateType) -> Disposable {let initial = DispatchTime.now() + startAfter
    var timerState = statelet timer = DispatchSource.makeTimerSource(queue: self.queue)
    timer.schedule(deadline: initial, repeating: period, leeway: self.leeway)
        
    var timerReference: DispatchSourceTimer? = timerlet cancelTimer = Disposables.create {
        timerReference?.cancel()
        timerReference = nil
    }

    timer.setEventHandler(handler: {if cancelTimer.isDisposed {return}
        timerState = action(timerState)
    })
    timer.resume()        return cancelTimer
}复制代码

看到这里一下就明白了,从刚开始的参数类型开始就一直和GCD有关联,原来就是用序列封装了 DispatchSourceTimer ,在定时器触发时发送序列的 .next 信号。在序列销毁时 canceltimer

26bqAju.png!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK