blog

iOSタイマー比較とグローバルタイマーパッケージ

NSTimer、GCDタイマーを含むiOSの一般的に使用されるタイマー、。これは主に、グローバルタイマーのカプセル化と同様に、3種類のタイマーの使用を紹介します。 最初のこれは、循環参照につながるため...

Mar 24, 2020 · 5 min. read
シェア

I. はじめに

iOSでよく使われるタイマーには、NSTimer、CADisplayLink、GCDタイマーがあります。今回は主に3種類のタイマーの使い方と、グローバルタイマーのカプセル化について紹介します。主な処理としては、タイマーの知識、マルチスレッドロックメッセージ転送などがあります。

第二に、使用

タイマー

なぜなら、selfはタイマーを強く参照し、タイマーはターゲットを強く参照するからです。

self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(test), userInfo: nil, repeats: true)

番目のこの方法は循環参照の問題を解決してくれますが、タイマーをランループに追加しないと正しく動作しないので注意が必要です。

self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: {[weak self] (timer) in
 self?.test()
})
self.timer = Timer.init(timeInterval: 1, repeats: true, block: {[weak self] (timer) in
 guard let self = self else { return }
 self.test()
 RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
})
self.timer?.fire()

CADisplayLink

初期化メソッド

self.link = CADisplayLink.init(target: self, selector: #selector(test))
self.link?.preferredFramesPerSecond = 1
self.link?.add(to: RunLoop.current, forMode: .default)

CADisplayLinkを使用する場合、タイマーと同じ問題があることに注意してください。

GCD タイマ

// タイマー作成
let queue = DispatchQueue.global()
self.gcdTimer = DispatchSource.makeTimerSource(queue: queue)
// self.gcdTimer?.schedule(wallDeadline: DispatchWallTime.now(), repeating: 1)
self.gcdTimer?.schedule(deadline: DispatchTime.now(), repeating: 1)
self.gcdTimer?.setEventHandler { [weak self] in
	self?.test()
}
self.gcdTimer?.resume()

GCDタイマーを使用するには、gcdTimerを保持するメンバ変数が必要です。

第三に、結論の比較

  • タイマーとCADisplayLinkの使用はrunloopの実行に依存し、runloopは同時に他のタスクを実行するため、結果としてタイムリーではなくなります。また、不適切な使用はメモリ・リークを引き起こしやすくなります。
  • GCDはシステム・カーネルに直接リンクされており、ランループに依存しません。

グローバルタイマーカプセル化

インターフェースパラメータ設計

  • グローバルタイマーは複数回実行される可能性があり、一意に識別する必要があります。
  • タイマー開始時間の設定
  • タイマー間隔の設定
  • タイマーを繰り返すかどうかの設定
  • タイマーを非同期に実行するかどうかを設定します。
  • コールバック
class TimerManager: NSObject {
	
 /// 各キーは、一意のタイマー、ソリッド辞書ストレージに対応する
 private var timers = NSMutableDictionary()
 /// これは、同時にマルチスレッドの読み取りと書き込みが含まれるため、エラーを避けるために、データの変更の実装では、操作をロックする必要がある
 private let semaphore = DispatchSemaphore.init(value: 1)
 static var instance: TimerManager {
 struct Static {
 static let instance: TimerManager = TimerManager()
 }
 return Static.instance
 }
 /// カウントダウンメッセージ転送
 /// - Parameters:
 /// - timerKey: カウントダウンキーは、一意であることを確認する必要がある
 /// - targat: メッセージ転送
 /// - selector:  
 /// - start: 開始時刻
 /// - interval: インターバル
 /// - repeats: 繰り返すかどうか
 /// - async: 非同期かどうか
 func schedule(timerKey: String, targat: NSObject, selector: Selector, start: DispatchTime = .now(), interval: TimeInterval = 1, repeats: Bool = true, async: Bool = true) {
 self.schedule(timerKey: timerKey, start: start, interval: interval, repeats: repeats, async: async) { [weak targat] in
 /// targatがセレクタに応答できるかどうかをチェックする。
 if targat?.responds(to: selector) ?? false {
 targat?.perform(selector)
 }
 }
 }
 /// カウントダウンブロック
 /// - Parameters:
 /// - timerKey: カウントダウンキーは、一意であることを確認する必要がある
 /// - start: 開始時刻
 /// - interval: インターバル
 /// - repeats: 繰り返すかどうか
 /// - async: 非同期かどうか
 /// - eventHandle:  
 func schedule(timerKey: String, start: DispatchTime = .now(), interval: TimeInterval = 1, repeats: Bool = true, async: Bool = true, eventHandle: @escaping (() -> Void)) {
 guard !timerKey.isEmpty || start.rawValue <= 0 || interval <= 0 else {
 return
 }
 let timerQueue = async ? DispatchQueue.global() : DispatchQueue.main
 let timer = DispatchSource.makeTimerSource(queue: timerQueue)
 semaphore.wait()
 timers[timerKey] = timer
 semaphore.signal()
 timer.schedule(deadline: start, repeating: interval)
 timer.setEventHandler { [weak self] in
 eventHandle()
 if !repeats {
 self?.cancelTask(timerKey: timerKey)
 }
 }
 timer.resume()
 }
 /// タイマーキャンセル
 /// - Parameter timerKey: タイマーID
 func cancelTask(timerKey: String) {
 guard !timerKey.isEmpty else {
 return
 }
 guard let timer = timers[timerKey] as? DispatchSourceTimer else {
 return
 }
 timer.cancel()
 semaphore.wait()
 timers.removeObject(forKey: timerKey)
 semaphore.signal()
 }
}

インタフェースのパラメータ設計

/// メッセージ転送
TimerManager.instance.schedule(timerKey: self.theClassName, targat: self, selector: #selector(test))
/// block 
TimerManager.instance.schedule(timerKey: self.theClassName) { [weak self] in
	self?.test()
}

グローバルタイマーは、アプリケーションがバックグラウンドに退避し、時間間隔で戻ってくることを考慮していません。

、通知を聞き、打ち上げ前後の時間差を比較し、データソースに変更を加えます。
、毎回NSDateを保存する時間のデータ処理時間()、データ処理の次の実行では、まず保存されたNSDateと現在の時差を比較し、データ処理インチコードは次のとおりです。
@objc func test() {
	/// currentCountが徐々に減っている場合は、ここで判断する必要がある。 0より小さい場合は、タイマーメソッドの破棄を実行する。
 // if currentCount <= 0 {
 	// TimerManager.instance.cancelTask(timerKey: self.theClassName)
 // }
	if CacheManager.instance.hasCachedValue(with: self.theClassName) {
		if let cacheDate = CacheManager.instance.valueWithCache(key: self.theClassName) as? NSDate {
 if cacheDate.timeIntervalSinceNow < -2 {
 ///時差は負の数であるため、カウントダウンであれば時差を加算し、その逆であれば時差を減算する。
 self.currentCount -= Int(cacheDate.timeIntervalSinceNow) + 1
 }
 }
 }
 CacheManager.instance.cacheData(NSDate(), withKey: self.theClassName)
 currentCount += 1
 print(currentCount)
}
Read next

Informatica アプリケーション情報ライフサイクル管理

アプリケーション情報ライフサイクル管理ソフトウェアは、IT組織がデータの増加をコスト効率よく管理し、レガシーシステムやアプリケーションを安全に段階的に廃止し、テストデータ管理を最適化し、機密データを保護するのに役立ちます。 データライフサイクルの各段階を管理することで、ITチームは情報ライフサイクル管理を改善し、アプリケーションの最適化、コンプライアンス、IT統合、合併・買収をよりよくサポートすることができます。

Mar 24, 2020 · 2 min read