CADisplayLink、NSTimer
- 有循环引用问题
- 如何解决?
1. 利用闭包,并弱引用其中的 self (建议使用这一种方式)
timer = Timer(fire: Date.distantPast, interval: 1, repeats: true) { [weak self] (timer) in
self?.test()
}
RunLoop.main.add(timer!, forMode: RunLoop.Mode.common)
下面这个类方法和上面的是一样的,应该就是对上面的封装,默认已经把timer加进runloop中了
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] (timer) in
self?.test()
}
2. 利用Proxy(Swift无法使用了,使用replaceMethod替代)
class HXTimerProxy: NSObject {
weak var target: NSObjectProtocol?
weak var timer: Timer?
var selector: Selector?
public required init(target: NSObjectProtocol?, selector: Selector?) {
self.target = target
self.selector = selector
super.init()
guard let target = target, let selector = selector, target.responds(to: selector) else {
return
}
let method = class_getInstanceMethod(self.classForCoder, #selector(HXTimerProxy.redirectionMethod))!
class_replaceMethod(self.classForCoder, selector, method_getImplementation(method), method_getTypeEncoding(method))
}
@objc func redirectionMethod () {
if let target = target {
target.perform(selector)
} else {
timer?.invalidate()
}
}
}
然后在对应的地方调用即可
let proxy = HXTimerProxy(target: aTarget as? NSObjectProtocol, selector: aSelector)
let timer = Timer(timeInterval: ti, target: proxy, selector: aSelector, userInfo: userInfo, repeats: yesOrNo)
proxy.timer = timer
RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
- 有不准确的问题
- 必须保证在一个活跃的 runloop
- 因为是基于Runloop的,所以每次循环执行完来的时间点是无法确定的,因为
- CADisplayLink,CPU负载的时候会影响触发事件,且触发事件大于触发间隔会导致掉帧现象
let timer = Timer(timeInterval: 1, target: self, selector: #selector(test), userInfo: nil, repeats: true)
@objc private func test() {
sleep(1)
print(CACurrentMediaTime())
}
执行的结果为
288462.109615096
288464.092074808
288466.092211654
288468.092896747
288470.093108874
也就是说他会 1s 的定时器会隔 2s 打印,这显然不符合要求。
利用GCD封装定时器
public class HXTimer {
private let sourceTimer: DispatchSourceTimer
public class func every(_ interval: DispatchTimeInterval, handle: @escaping () -> Void) -> HXTimer {
let timer = HXTimer(interval: interval, repeats: true, handler: handle)
timer.start()
return timer
}
public init(interval: DispatchTimeInterval, deadline: DispatchTime = .now(), repeats: Bool = false, queue: DispatchQueue? = nil , handler: @escaping () -> Void) {
sourceTimer = DispatchSource.makeTimerSource(queue: queue ?? DispatchQueue(label: "com.hxtimer.queue"))
sourceTimer.schedule(deadline: deadline, repeating: repeats ? interval : .never, leeway: .milliseconds(10))
sourceTimer.setEventHandler(handler: handler)
}
deinit {
cancel()
}
}
extension HXTimer {
public func start() {
sourceTimer.resume()
}
public func cancel() {
sourceTimer.setEventHandler(handler: nil)
sourceTimer.cancel()
}
}
- Timer线程不安全
- 状态未判断,如果多次调用会发生闪退