網站首頁 編程語言 正文
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
相關推薦
- 2022-04-23 arguments獲取當前所在函數
- 2023-07-05 Redis性能瓶頸:如何優化大key問題?
- 2022-09-25 Spring核心IOC的核心類解析
- 2022-08-11 python?rpyc客戶端調用服務端方法的注意說明_python
- 2021-12-09 C語言實現將double/float?轉為字符串(帶自定義精度)_C 語言
- 2022-06-01 C語言線性表中順序表超詳細理解_C 語言
- 2022-03-16 Linux系統中日志詳細介紹_Linux
- 2022-08-05 詳解C#通過反射獲取對象的幾種方式比較_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支