一緒に書く習慣を身につけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して10日目です。クリックしてイベントの詳細をご覧ください。
iOSで一般的に使用されるタイマーには、タイマー、CADisplayLink、DispatchSourceTimerの3種類があります。
タイマー
タイマーは最も一般的なタイマーです。タイマーが作成されると(スケジュールされていないため、手動でrunloopに追加する必要があります)、初期化中に指定されたt秒後にタイマーが自動的にトリガーされます。私たちがよく抱える問題の2つは、精度と循環参照の問題です。
精度について
メインランループにタイマーを追加すると、メインスレッドがさまざまなUI操作や複雑な操作でビジー状態になり、NSTimerの実行が遅れて精度が低下するため、スレッドをブロックしやすくなります。
タイマーがより高い精度を望む場合、次の側面を考慮することができます
-
適切なモードを選択してください
タイマーには、defaultRunLoopModeとUITrackingRunLoopModeの2つの共通モードがあります。ページがスクロールされるとUITrackingRunLoopModeが呼び出されるため、通常、ビジネス開発中に適切なモードを選択します。
.commonModesは単なるフラグです。つまり、タイマーはdefaultRunLoopModeとUITrackingRunLoopModeの両方で有効です。
- 子RunLoopにタイマーを追加する
DispatchQueue.global().async {
let timer = Timer(timeInterval: 2, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
RunLoop.current.add(timer, forMode: .commonModes)
CFRunLoopRun()
}
@objc func update() {
print("update")
if isStop {
CFRunLoopStop(CFRunLoopGetCurrent())
}
}
复制代码
循環参照について
タイマーは、target-actionとblockの2つの方法で呼び出すことができます。その中で、target-actionは、target、action、runLoopの間に強力な参照チェーンがあるため、循環参照を引き起こし、メモリリークを引き起こします。ソリューション
1。ブロックコールバックメソッドを使用する
Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] in
guard let self = self else { return }
}
复制代码
2.NSProxyクラスを中間オブジェクトとして使用します
CADisplayLink
CADisplayLinkは、画面が更新されるのと同じ頻度でコンテンツを画面に表示します。また、NSRunLoopに依存して実行されます。iOSデバイスの画面のリフレッシュレートは固定されています。CADisplayLinkは通常、各更新の最後に呼び出されます。精度が高く、画面の更新などに適しています。
var displaylink: CADisplayLink?
func create() {
let displaylink = CADisplayLink(target: self,
selector: #selector(refresh))
displaylink.add(to: .current,
forMode: .defaultRunLoopMode)
}
@objc func refresh(displaylink: CADisplayLink) {
// 打印时间戳
print(displaylink.timestamp)
}
// 停止CADisplayLink
func stop() {
displaylink?.invalidate()
displaylink = nil
}
复制代码
DispatchSourceTimer
DispatchSourceTimerはシステムレベルであり、RunLoopの影響を受けないため、高精度です。
一般的な基本的な使用法
// 创建源时间
public class func makeTimerSource(flags: DispatchSource.TimerFlags = [], queue: DispatchQueue? = nil) -> DispatchSourceTimer
// 设置定时器事件
public func setEventHandler(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], handler: Self.DispatchSourceHandler?)
// 设置定时器触发的回调
/*
deadline: 表示计时开始时间
interval: 间隔时间
leeway : 精准度
*/
public func schedule(deadline: DispatchTime, interval: DispatchTimeInterval = .never, leeway: DispatchTimeInterval = .nanoseconds(0))
// 销毁
public func cancel()
// 启动
public func resume()
// 暂停
public func suspend()
复制代码
let internalTimer = DispatchSource.makeTimerSource(queue: queue)
internalTimer.setEventHandler { [weak self] in
guard let self = self else { return }
handler(self)
}
if repeats {
internalTimer.schedule(deadline: .now()+interval, repeating: interval, leeway: leeway)
} else {
internalTimer.schedule(deadline: .now()+interval, leeway: leeway)
}
internalTimer.resume()
复制代码