iOSのタイマー

一緒に書く習慣を身につけましょう!「ナゲッツデイリーニュープラン・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()
复制代码

おすすめ

転載: juejin.im/post/7085610285876117517