日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

iOS?DispatchSourceTimer?定時器的具體使用_IOS

作者:大番薯醬 ? 更新時間: 2022-07-01 編程語言

1. 概述

說起計時器,很多開發人員第一時間就會想起Timer,但是隨著使用的深入,慢慢就發現Timer其實不是很好用,比如說TableView滑動時候不執行,Timer循環應用。

2. DispatchSourceTimer

DispatchSourceTimer,也就是大家通常叫的GCD Timer,是依賴于GCD的一種Timer,Runloop的底層代碼中也用到這種Timer,可見GCD Timer并不依賴與Runloop。

先看一下蘋果的定義:

A dispatch source that submits the event handler block based on a timer.

2.1 GCD Timer 創建

使用下面的方法即可創建一個DispatchSourceTimer對象。

class func makeTimerSource(flags: DispatchSource.TimerFlags = [], queue: DispatchQueue? = nil) -> DispatchSourceTimer
// 默認在主隊列中調度使用
let timer = DispatchSource.makeTimerSource()
// 指定在主隊列中調度使用
let timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
// 指定在全局隊列中調度使用
let timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.global())
// 指定在自定義隊列中調度使用
let customQueue = DispatchQueue(label: "customQueue")
let timer = DispatchSource.makeTimerSource(flags: [], queue: customQueue)

2.2 GCD Timer 配置

配置Timer參數,需要使用DispatchSourceTimer協議的方法。可以安排一次或多次觸發的Timer。Timer每次觸發的時候,都會調用已部署的任務。

    // 從現在開始,每秒執行一次。
    timer?.schedule(deadline: DispatchTime.now(), repeating: .seconds(1), leeway: .nanoseconds(1))
    // 5秒之后執行任務,不重復。
    timer?.schedule(deadline: DispatchTime.now() + 5, repeating: .never, leeway: .nanoseconds(1))

2.3 GCD Timer 部署任務

當Timer配置完參數后,使用DispatchSourceProtocol協議的方法來部署要執行的任務。

setEventHandler和setRegistrationHandler的區別:

  • setEventHandler:給Timer設置要執行的任務,包括一次性任務和定時重復的任務。回調方法在子線程中執行。
  • setRegistrationHandler:這個方法設置的任務只會執行一次,也就是在Timer就緒后開始運行的時候執行,類似于Timer開始的一個通知回調。回調方法在子線程中執行。

例如下面的代碼:

var timer: DispatchSourceTimer?

func initTimer() {
    // 默認在主隊列中調度使用
    timer = DispatchSource.makeTimerSource()
    
    // 從現在開始,每秒執行一次。
    timer?.schedule(deadline: DispatchTime.now(), repeating: .seconds(1), leeway: .nanoseconds(1))
    // 5秒之后執行任務,不重復。

// timer?.schedule(deadline: DispatchTime.now() + 5, repeating: .never, leeway: .nanoseconds(1))

    timer?.setEventHandler {
        DispatchQueue.main.async {
            print("執行任務")
        }
    }
    
    timer?.setRegistrationHandler(handler: {
        DispatchQueue.main.async {
            print("Timer開始工作了")
        }
    })
    timer?.activate()
}

執行結果如下:

2020-11-28 02:20:00 +0000 Timer開始工作了
2020-11-28 02:20:00 +0000 執行任務
2020-11-28 02:20:01 +0000 執行任務
2020-11-28 02:20:02 +0000 執行任務

2.4 GCD Timer控制方法

下面看一下Timer的一下控制方法及狀態:

  • activate() : 當創建完一個Timer之后,其處于未激活的狀態,所以要執行Timer,需要調用該方法。
  • suspend() : 當Timer開始運行后,調用該方法便會將Timer掛起,即暫停。
  • resume() : 當Timer被掛起后,調用該方法便會將Timer繼續運行。
  • cancel() : 調用該方法后,Timer將會被取消,被取消的Timer如果想再執行任務,則需要重新創建。

上面的這些方法如果使用不當,很容易造成APP崩潰,下面來看一下具體注意事項及建議:

  • 當Timer創建完后,建議調用activate()方法開始運行。如果直接調用resume()也可以開始運行。
  • suspend()的時候,并不會停止當前正在執行的event事件,而是會停止下一次event事件。
  • 當Timer處于suspend的狀態時,如果銷毀Timer或其所屬的控制器,會導致APP奔潰。
  • 2020-11-28 02:20:00 +0000 Timer開始工作了
    2020-11-28 02:20:00 +0000 執行任務
    2020-11-28 02:20:01 +0000 執行任務
    2020-11-28 02:20:02 +0000 執行任務
    suspend()和resume()需要成對出現,掛起一次,恢復一次,如果Timer開始運行后,在沒有suspend的時候,直接調用resume(),會導致APP崩潰。
  • 使用cancel()的時候,如果Timer處于suspend狀態,APP崩潰。
  • 另外需要注意block的循環引用問題。

2.5 雙重循環 DispatchSourceTimer

比如:我們需要一定時間情況下(數組長度*4),每隔一段時間打印對應下標(每個元素隔4秒打印),無限打印

在下面例子的雙重循環中使用 DispatchSourceTimer 你會發現print只打印了 dom some = 0 這個不是我們想要的效果

var exaltedTimer: DispatchSourceTimer?

func demo {
    let arr = [1,2,3,4]

    self.exaltedTimer = DispatchSource.makeTimerSource()
    self.exaltedTimer?.schedule(deadline: .now(), repeating: TimeInterval(arr.count*4))

    self.exaltedTimer?.setEventHandler(handler: {

        for (index, item) in arr.enumerated() {

            let timer2 = DispatchSource.makeTimerSource()
            timer2.schedule(deadline: .now()+4.0*CGFloat(index), repeating: .infinity)
            timer2.setEventHandler {
                DispatchQueue.main.async {
                    print("do some = \(index)")
                }
            }
            
            timer2.activate()


        }
    })
    self.exaltedTimer?.activate()
}

這個是因為timer2使用之后就被釋放了,所以要cancel和resume配合使用,這樣就實現了我們想要的效果了。

var exaltedTimer: DispatchSourceTimer?

var exaltedTimerArray: [DispatchSourceTimer] = []

func demo {

    self.exaltedTimer?.cancel()
    for subTimer in self.exaltedTimerArray {
        subTimer.cancel()
    }

    let arr = [1,2,3,4]

    self.exaltedTimer = DispatchSource.makeTimerSource()
    self.exaltedTimer?.schedule(deadline: .now(), repeating: TimeInterval(arr.count*4))

    self.exaltedTimer?.setEventHandler(handler: {

        for (index, item) in arr.enumerated() {

            let timer2 = DispatchSource.makeTimerSource()
            timer2.schedule(deadline: .now()+4.0*CGFloat(index), repeating: .infinity)
            timer2.setEventHandler {
                DispatchQueue.main.async {
                    print("do some = \(index)")
                }
            }
            
            self.exaltedTimerArray.append(timer2)
            timer2.resume()


        }
    })
    self.exaltedTimer?.resume()

參考文章

https://blog.csdn.net/guoyongming925/article/details/110224064

原文鏈接:https://juejin.cn/post/7094458036315652133

欄目分類
最近更新