iOS中如何正確釋放GCD定時器(dispatch_source_t)以及防止Crash分歇?

Dispatch Source Timer 的使用以及注意事項

Dispatch Source Timer 是一種與 Dispatch Queue 結(jié)合使用的定時器织阳。當需要在后臺 queue 中定期執(zhí)行任務(wù)的時候仿贬,使用 Dispatch Source Timer 要比使用 NSTimer 更加自然豁跑,也更加高效(無需在 main queue 和后臺 queue 之前切換)。

使用如下:

@property (nonatomic,strong) dispatch_source_t timer;

/** 創(chuàng)建定時器對象

* para1: DISPATCH_SOURCE_TYPE_TIMER 為定時器類型

* para2-3: 中間兩個參數(shù)對定時器無用

* para4: 最后為在什么調(diào)度隊列中使用

*/

_gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));

/** 設(shè)置定時器

* para2: 任務(wù)開始時間

* para3: 任務(wù)的間隔

* para4: 可接受的誤差時間,設(shè)置0即不允許出現(xiàn)誤差

* Tips: 單位均為納秒

*/

dispatch_source_set_timer(_gcdTimer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0.0 * NSEC_PER_SEC);

/** 設(shè)置定時器任務(wù)

* 可以通過block方式

* 也可以通過C函數(shù)方式

*/

dispatch_source_set_event_handler(_gcdTimer, ^{

? ? static int gcdIdx = 0;

? ? NSLog(@"GCD Method: %d", gcdIdx++);

? ? NSLog(@"%@", [NSThread currentThread]);

});

// 啟動任務(wù)单料,GCD計時器創(chuàng)建后需要手動啟動

dispatch_resume(_gcdTimer);

如果需要暫停定時器調(diào)用:

dispatch_suspend(timer);

dispatch_suspend 和 dispatch_resume 應(yīng)該是成對出現(xiàn)的。兩者分別會減少和增加 dispatch 對象的掛起計數(shù)点楼,但是沒有 API 獲取當前是掛起還是執(zhí)行狀態(tài)扫尖,所以需要自己記錄。


dispatch_suspend 暫停隊列并不意味著當前執(zhí)行的 block 暫停

當暫停派發(fā)隊列時需要注意掠廓,調(diào)用 dispatch_suspend 暫停一個隊列换怖,并不意味著暫停當前正在執(zhí)行的 block,而是 block 可以執(zhí)行完蟀瞧,但是接下來的 block 會被暫停沉颂,直到 dispatch_resume 被調(diào)用。

dispatch_suspend 狀態(tài)下無法釋放

如果調(diào)用 dispatch_suspend 后 timer 是無法被釋放的悦污。一般情況下會發(fā)生崩潰并報“EXC_BAD_INSTRUCTION”錯誤铸屉,看下?GCD 源碼dispatch source release 的時候判斷了當前是否是在暫停狀態(tài)。

所以切端,dispatch_suspend 狀態(tài)下直接釋放當前控制器或者釋放定時器彻坛,會導致定時器崩潰。

并且初始狀態(tài)(未調(diào)用dispatch_resume)踏枣、掛起狀態(tài)昌屉,都不能直接調(diào)用dispatch_source_cancel(timer),調(diào)用就會導致app閃退茵瀑。

建議一:盡量不使用dispatch_suspend间驮,在dealloc方法中,在dispatch_resume狀態(tài)下直接使用dispatch_source_cancel來取消定時器马昨。

建議二:使用懶加載創(chuàng)建定時器蜻牢,并且記錄當timer 處于dispatch_suspend的狀態(tài)烤咧。這些時候,只要在 調(diào)用dealloc 時判斷下抢呆,已經(jīng)調(diào)用過 dispatch_suspend 則再調(diào)用下 dispatch_resume后再cancel煮嫌,然后再釋放timer。


停止 Timer

停止 Dispatch Timer 有兩種方法抱虐,一種是使用?dispatch_suspend昌阿,另外一種是使用?dispatch_source_cancel。

dispatch_suspend?嚴格上只是把 Timer 暫時掛起恳邀,它和?dispatch_resume?是一個平衡調(diào)用懦冰,兩者分別會減少和增加 dispatch 對象的掛起計數(shù)。當這個計數(shù)大于 0 的時候谣沸,Timer 就會執(zhí)行刷钢。在掛起期間,產(chǎn)生的事件會積累起來乳附,等到 resume 的時候會融合為一個事件發(fā)送内地。

dispatch_source_cancel?則是真正意義上的取消 Timer。被取消之后如果想再次執(zhí)行 Timer赋除,只能重新創(chuàng)建新的 Timer阱缓。這個過程類似于對 NSTimer 執(zhí)行?invalidate。

關(guān)于取消 Timer举农,另外一個很重要的注意事項荆针,dispatch_suspend?之后的 Timer,是不能被釋放的颁糟!下面的代碼會引起崩潰:

- (void)dealloc

{

? ? dispatch_suspend(_timer);

_timer =nil;// EXC_BAD_INSTRUCTION 崩潰

}

因此使用?dispatch_suspend?時航背,Timer 本身的實例需要一直保持。使用?dispatch_source_cancel?則沒有這個限制:


- (void)dealloc

{

? ? dispatch_source_cancel(_timer);

_timer =nil;// OK

}

總結(jié):

Dispatch Source使用最多的就是用來實現(xiàn)定時器棱貌,source創(chuàng)建后默認是暫停狀態(tài)玖媚,需要手動調(diào)用dispatch_resume啟動定時器。

Dispatch Source定時器使用時也有一些需要注意的地方键畴,不然很可能會引起crash:

1最盅、循環(huán)引用:因為dispatch_source_set_event_handler回調(diào)是個block,在添加到source的鏈表上時會執(zhí)行copy并被source強引用起惕,如果block里持有了self涡贱,self又持有了source的話,就會引起循環(huán)引用惹想。正確的方法是使用weak+strong或者提前調(diào)用dispatch_source_cancel取消timer问词。

2、dispatch_resume和dispatch_suspend調(diào)用次數(shù)需要平衡嘀粱,如果重復(fù)調(diào)用dispatch_resume則會崩潰,因為重復(fù)調(diào)用會讓dispatch_resume代碼里if分支不成立激挪,從而執(zhí)行了DISPATCH_CLIENT_CRASH("Over-resume of an object")導致崩潰辰狡。

3、source在suspend狀態(tài)下垄分,如果直接設(shè)置source = nil或者重新創(chuàng)建source都會造成crash宛篇。正確的方式是在resume狀態(tài)下調(diào)用dispatch_source_cancel(source)釋放當前的source。

4薄湿、盡量不使用dispatch_suspend叫倍,在dealloc方法中,在dispatch_resume狀態(tài)下直接使用dispatch_source_cancel來取消定時器豺瘤。

5吆倦、使用懶加載創(chuàng)建定時器,并且記錄當timer 處于dispatch_suspend的狀態(tài)坐求。這些時候蚕泽,只要在 調(diào)用dealloc 時判斷下,已經(jīng)調(diào)用過 dispatch_suspend 則再調(diào)用下 dispatch_resume后再cancel桥嗤,然后再釋放timer须妻。

本文轉(zhuǎn)載自??https://blog.csdn.net/u013602835/article/details/8762349

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市砸逊,隨后出現(xiàn)的幾起案子璧南,更是在濱河造成了極大的恐慌掌逛,老刑警劉巖师逸,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異豆混,居然都是意外死亡篓像,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門皿伺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來员辩,“玉大人,你說我怎么就攤上這事鸵鸥〉旎” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵妒穴,是天一觀的道長宋税。 經(jīng)常有香客問我,道長讼油,這世上最難降的妖魔是什么杰赛? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮矮台,結(jié)果婚禮上乏屯,老公的妹妹穿的比我還像新娘根时。我一直安慰自己,他們只是感情好辰晕,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布蛤迎。 她就那樣靜靜地躺著,像睡著了一般含友。 火紅的嫁衣襯著肌膚如雪忘苛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天唱较,我揣著相機與錄音扎唾,去河邊找鬼。 笑死南缓,一個胖子當著我的面吹牛胸遇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播汉形,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼纸镊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了概疆?” 一聲冷哼從身側(cè)響起逗威,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎岔冀,沒想到半個月后凯旭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡使套,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年罐呼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侦高。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡嫉柴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出奉呛,到底是詐尸還是另有隱情计螺,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布瞧壮,位于F島的核電站登馒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏馁痴。R本人自食惡果不足惜谊娇,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧济欢,春花似錦赠堵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至半等,卻和暖如春揍愁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背杀饵。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工莽囤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人切距。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓朽缎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谜悟。 傳聞我的和親對象是個殘疾皇子话肖,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容