選擇 GCD 還是 NSTimer ?

我們常常會延遲某件任務的執行,或者讓某件任務周期性的執行。然后也會在某些時候需要取消掉之前延遲執行的任務。

?

延遲操作的方案一般有三種:

?

1.NSObject的方法:

?

?

2.使用NSTimer的方法:

?

?

3.使用GCD的方法:

?

?

一般情況下,我們選擇使用GCD的dispatch_after。

?

因為如果不用GCD,編碼需要注意以下三個細節:

?

1.必須保證有一個活躍的runloop。

?

performSelector和scheduledTimerWithTimeInterval方法都是基于runloop的。我們知道,當一個應用啟動時,系統會開啟一個主線程,并且把主線程的runloop激活,也就是run起來,并且主線程的runloop是不會停止的。所以,當這兩個方法在主線程可以被正常調用。但情況往往不是這樣的。實際編碼中,我們更多的邏輯是放在子線程中執行的。而子線程的runloop是默認關閉的。這時如果不手動激活runloop,performSelector和scheduledTimerWithTimeInterval的調用將是無效的。

?

2.NSTimer的創建與撤銷必須在同一個線程操作、performSelector的創建與撤銷必須在同一個線程操作。

?

3.內存管理有潛在泄露的風險

?

scheduledTimerWithTimeInterval方法將target設為A對象時,A對象會被這個timer所持有,也就是會被retain一次,timer會被當前的runloop所持有。performSelector:withObject:afterDelay:方法實際上是在當前線程的runloop里幫你創建的一個timer去執行任務,所以和scheduledTimerWithTimeInterval方法一樣會retain其調用對象。但是,我們往往不希望因為這些延遲操作而影響對象的生命周期,更甚至是,導致對象無法釋放。舉個例子:

?

?

你創建的對象X中有一個實例變量_timer,方法fireTimer觸發一個timer,方法cancel取消這個timer。在對象X需要銷毀的時候,需要將它的timer取消掉。

?

?

不幸的是,dealloc方法將永遠不會被調用。因為timer的引用,對象X的引用計數永遠不會降到0,dealloc方法也就不會被調用。這時如果不調用cancel,對象X將永遠無法釋放,造成內存泄露。想想一個對象若不調用某一個方法就會造成內存泄露,這該是多大一個坑。

?

This method is the only way to remove a timer from an?NSRunLoop?object. The?NSRunLoop?object removes its strong reference to the timer, either just before the?invalidate?method returns or at some later point.

If it was configured with target and user info objects, the receiver removes its strong references to those objects as well.

You must send this message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.

?

上面摘自蘋果官方文檔對invalidate方法的解釋。可以看到,當一個timer被schedule的時候,timer會持有target對象,NSRunLoop對象會持有timer。當invalidate被調用時,NSRunLoop對象會釋放對timer的持有,timer會釋放對target的持有。除此之外,我們沒有途徑可以釋放timer對target的持有。所以解決內存泄露就必須撤銷timer,若不撤銷,target對象將永遠無法釋放。

?

若使用dispatch_after,系統會幫我們處理線程級的邏輯,這樣也我們更易于享受系統對線程所做的優化。除此之外,我們不用關心runloop的問題。并且調用的對象也不會被強行持有,這樣上述的內存問題也不復存在。當然,需要注意block會持有其傳入的對象,但這可以通過weakself解決。所以在這種延遲操作方案中,使用dispatch_after更佳。

?

但是呢,dispatch_after有個致命的弱點:dispatch_after一旦執行后,就不能撤銷了。而performSelector可以使用cancelPreviousPerformRequestsWithTarget方法撤銷,NSTimer也可以調用invalidate進行撤銷。(注意:撤銷任務與創建timer任務必須在同一個線程,即同一個runloop)所以我們還是得用NSTimer或者performSelector嗎?

?

NO,其實GCD也有timer的功能。用GCD來實現一個timer:

?

?

這樣我們就規避了NSTimer的三個缺陷。

?

到這里問題基本得到了解決,但是我們還可以做的更好:)

?

1.GCD的timer使用的API比較冗余,每次使用都會copy代碼。2.沒有repeats的選項,若只想執行一次還得自己寫標記位控制。這些問題我們都可以封裝成一個統一的API:

?

?

這樣,外部只需調用這個兩個接口,用起來和NSTimer一樣方便!

?

?

上面的代碼就創建了一個名叫myTimer的timer,這個timer將在2 seconds后執行一個block,隨后timer自動停止并被釋放。當然,如果repeats參數傳入的是YES,那么這么timer會一個周期接一個周期的執行,直到你cancel掉這個timer。

?

當然,你可以在self對象的dealloc方法里面做cancel,這樣保證了timer恰好運行于整個對象的生命周期中。這是NSTimer和performSelector所做不到的事情。你也可以通過queue參數控制這個timer所添加到的線程,也就是action最終執行的線程。傳入nil則會默認放到子線程中執行。UI相關的操作需要傳入dispatch_get_main_queue()以放到主線程中執行。

?

Well, we can actually do even better.

?

注意到,我們經常遇到的場景是,在開始新一次計時的時候,取消掉上一次的計時。也就是每次schedule之前先cancel。這部分對任務處理的能力,也是可以集成到我們的組件中的。我們可以向外部提供一個枚舉類型的選項,以選擇其對任務的處理類型:

?

?

或者場景是,在開始新一次計時的時候,取消上一次的計時,但是將上一次計時的任務,合并到新的一次計時中,最終一并執行。

?

針對這兩種場景,JX_GCDTimerManager提供了兩個option選項:

?

?

如果你不care這些使用場景的話,默認使用AbandonPreviousAction就行了。需要注意的是,同一個timer建議保持同一個任務處理方式,即相同的ActionOption,如果需要切換option,請注意一下切換的銜接問題。

?

大家也可以自行去對actionOption做擴展,以滿足常見的使用場景。

?

詳細的實現,見git源碼。

?

綜上,選擇使用GCD的技術有助于我們提高代碼的健壯性與穩定性。

?

注:文中若有錯誤,恭請斧正哈!:)

?

源碼地址:https://github.com/Joeyqiushi/JX_GCDTimer

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/284155.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284155.shtml
英文地址,請注明出處:http://en.pswp.cn/news/284155.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

完美解決Idea unable to access git 錯誤

在命令行執行 如下命令即可 git config --global --unset http.proxy git config --global --unset https.proxy

Web框架 性能評測 -- C# 的性能 和 Rust、C++并駕齊驅

自從2021年2月第20輪公布的測試以后,一年半后 的2022年7月19日 發布了 TechEmpower 21輪測試報告:Round 21 results - TechEmpower Framework Benchmarks。Techempower benchmark是包含范圍最廣泛的web框架性能測試,覆蓋了比較典型的使用場景…

CF449 C. Jzzhu and Apples

1 /*2 http://codeforces.com/problemset/problem/449/C3 cf 449 C. Jzzhu and Apples4 數論素數貪心5 */6 #include <cstdio>7 #include <algorithm>8 using namespace std;9 const int Nmax100005; 10 int is_prime[Nmax]; 11 int book[Nmax]; 12 int cnt[Nmax];…

【GlobalMapper精品教程】027:路徑剖面和和視線工具的使用

文章目錄 一、路徑剖面簡介二、創建剖面圖1. 加載DEM2. 創建剖面圖3. 計算填挖方3. 保存剖面圖一、路徑剖面簡介 路徑剖面視線工具允許您使用加載的高程數據集沿用戶指定的路徑獲取垂直剖面。 要定義生成3D路徑剖面所遵循的路徑,只需單擊鼠標左鍵選擇路徑的點,然后石鍵單擊…

QT中VideoProbe的簡介和實現

一、遇到問題在Android機上使用QT進行圖像處理程序設計的時候&#xff0c;遇到的一個比較明顯的問題就是圖片采集的問題----攝像頭獲得是實時的視頻&#xff0c;如果我們想從中動態地截獲圖片&#xff0c;并且轉換成Mat的格式&#xff0c;那么僅僅是靜態的imagecapturee就無法完…

bzoj2751[HAOI2012]容易題(easy)

bzoj2751[HAOI2012]容易題(easy) 題意&#xff1a; 已知一個數列A對于所有的A[i]都是1~n的自然數&#xff0c;一些A[i]不能取一些值&#xff0c;求出所有可能的數列的積的和 mod 1000000007的值。 題解&#xff1a; 題目中的n≤109實際上是109……首先推個方程s[l,r]s[l,k]*s[k…

WinForm(二):WinFrom中Main函數的入參和出參

基本上有獨立進程的應用&#xff0c;都是以Main函數作為入口&#xff0c;開始運行的。在C#中&#xff0c;Main函數可以無參無返回值&#xff0c;當然也可以是有string[]參數和int返返回值的。WinFrom也滿足這個規則。那么Main作為一個進程的開始函數&#xff0c;那么是誰傳這些…

linux內存回收機制

無論計算機上有多少內存都是不夠的&#xff0c;因而linux kernel需要回收一些很少使用的內存頁面來保證系統持續有內存使用。頁面回收的方式有頁回寫、頁交換和頁丟棄三種方式&#xff1a;如果一個很少使用的頁的后備存儲器是一個塊設備&#xff08;例如文件映射&#xff09;&a…

編譯源碼 JAVA out of memory

轉載于:https://www.cnblogs.com/dyufei/p/6612032.html

安卓 Input Events(輸入事件)

在安卓中&#xff0c;有不止一種方法從你的應用截取用戶交互事件。在你的用戶界面中考慮事件&#xff0c;途徑就是從用戶界面中的一個指定的view對象中捕獲事件。該view提供了這樣做的方法。 在你用來組成你布局的不同的view類中&#xff0c;你或許注意到了一些公共的回調方法似…

【GlobalMapper精品教程】029:柵格重分類案例詳解

重分類就是對原有柵格像元值重新分類從而得到一組新值并輸出。重分類工具有多種方法將像元值重新分類或更改為替代值,Globalmapper提供了柵格重分類的功能。 文章目錄 一、柵格重分類簡介二、柵格重分類案例【參考閱讀】:ArcGIS實驗教程——實驗四十三:ArcGIS柵格重分類(Re…

Mybatis 和 JPA 用哪個好? 優缺點 ?

本文不會下關于 Mybatis 和 JPA 兩個持久層框架哪個更好這樣的結論。只是擺事實&#xff0c;講道理&#xff0c;所以&#xff0c;請各位看官勿噴。 一、事件起因 關于 Mybatis 和 JPA 孰優孰劣的問題&#xff0c;爭論已經很多年了。一直也沒有結論&#xff0c;畢竟每個人的喜…

SkiaSharp 之 WPF 自繪 五環彈動球(案例版)

此案例基于拖曳和彈動球兩個技術功能實現&#xff0c;如有不懂的可以參考之前的相關文章&#xff0c;屬于遞進式教程。五環彈動球好吧&#xff0c;名字是我起的&#xff0c;其實&#xff0c;你可以任意個球進行聯動彈動&#xff0c;效果還是很不錯的&#xff0c;有很多前端都是…

【GlobalMapper精品教程】032:瀏覽地理照片及航線信息(航測應用)

本文講述globalmapper軟件在無人機航測了內業處理中的應用之:瀏覽地理照片及航線信息、相機參數、元數據編輯器。 文章目錄 1. 航線信息瀏覽2. 地理圖像瀏覽2.1 數字化工具2.2 要素信息工具2.3 屬性表3. 照片原數據編輯1. 航線信息瀏覽 打開globalmapper軟件,加載無人機航測…

Spring Boot 2.7.0發布,2.5停止維護

這幾天是Spring版本日&#xff0c;很多Spring工件都發布了新版本&#xff0c; Spring Framework 6.0.0 發布了第 4 個里程碑版本&#xff0c;此版本包含所有針對 5.3.20 的修復補丁&#xff0c;以及特定于 6.0 分支的 39 項修復和改進。而今天Spring Boot 2.7.0和Spring Securi…

【GlobalMapper精品教程】031:Globalmapper在航測內業數據處理中的應用舉例

Globalmapper在航測內業數據處理中的應用舉例索引。 文章目錄 1. 圖像及航線瀏覽2. 3D重建3. 點云分類4. 創建地形5. 地形分析1. 圖像及航線瀏覽 擴展閱讀:【GlobalMapper精品教程】032:瀏覽地理照片及航線信息(航測應用) 2. 3D重建 從Global Mapper的19版本開始,Pixels-…

移動工具V和選區工具M

移動工具快捷鍵&#xff1a;V 屬性&#xff1a; 自動選擇 在默認情況下&#xff0c;移動工具的“自動選擇”一項是沒有勾選的。表示只能選中圖層窗口中選定的固定圖層&#xff0c;不能隨意的點擊選擇別的圖層。在這里&#xff0c;我們也勾選“自動選擇”&#xff0c;可任意選擇…

SeleniumWebDriver擴展插件開發

Selenium WebDriver 是一組開源 API&#xff0c;用于自動測試 Web 應用程序&#xff0c;利用它可以通過代碼來控制chrome edge等瀏覽器&#xff01;有時候我們需要mock接口的返回&#xff0c;或者攔截和轉發請求&#xff0c;今天就來實現這個功能本插件代碼已開源&#xff1a;h…

ZooKeeper的工作原理

ZooKeeper是一個分布式的應用程序協調服務。 2 ZooKeeper的工作原理 Zookeeper 的核心是原子廣播&#xff0c;這個機制保證了各個Server之間的同步。實現這個機制的協議叫做Zab(Zookeeper Atomic Broadcast)協議。Zab協議有兩種模式&#xff0c;它們分別是恢復模式&#xff08;…