iOS10定時消息的改動

姓名:雷瀟 16030110083????

轉(zhuǎn)載自:http://www.lai18.com/content/24631425.html

【嵌牛導讀】iOS10已經(jīng)發(fā)布了一段時間泽疆,iOS10的各種適配相信大家已經(jīng)完成竞思。本文將講述的是關(guān)于iOS10內(nèi)核的一個小改動,慣例,本文屬于進階性技術(shù)文艳吠,不會講解API的使用主卫,要求讀者對RunLoop有一定的認知,感謝網(wǎng)友@送你的獨白么 提供的SDK虾宇。

【嵌牛鼻子】ios系統(tǒng)搓彻, runloop

【嵌牛提問】ios10怎么進行定是消息的改動?

【嵌牛正文】當我們的程序需要定時處理一些事件時嘱朽,我們就會用到定時器旭贬,常用的定時器有NSTimer,CADisplayLink搪泳,GCD Timer稀轨,本文主要針對NSTimer和CADisplayLink進行講述,因為這兩者跟你的Application更為密切岸军。

NSTimer和CADisplayLink都是建立在CFRunLoopTimer之上的抽象物奋刽,但有趣的是,蘋果只提供了NSTimer和CFRunLoopTimer互轉(zhuǎn)的Toll-Free Bridge艰赞,并沒有提供CADisplayLink和CFRunLoopTimer互轉(zhuǎn)的接口佣谐,因此一些開發(fā)者對此產(chǎn)生了一些猜想,有的人認為方妖,CADisplayLink是用GCD Dispatch Source來實現(xiàn)的狭魂,有的人認為,CADisplayLink是用RunLoopSource來實現(xiàn)的,但這些猜想的依據(jù)都太容易被推翻了雌澄。如果CADisplayLink是用GCD Dispatch Source來實現(xiàn)的斋泄,那么CADisplayLink是怎么在你所創(chuàng)建的子線程中工作的呢?如果CADisplayLink是用RunLoopSource來實現(xiàn)的掷伙,會不會多此一舉是己?

CFRunLoopTimer是RunLoop的定時源,與Source1(Port)一樣任柜,都屬于端口事件源卒废,但不同的是,每一個Source1都有與之對應的端口宙地,而一個RunLoopMode中的所有CFRunLoopTimer共用一個端口(Mode Timer Port)摔认,CFRunLoopTimer在RunLoop中的工作原理如下圖。

定時源工作 從定時源在RunLoop中的工作原理我們得知宅粥,只要符合條件的定時器都會被觸發(fā)参袱,也就是說,在同一次Loop中秽梅,可能會執(zhí)行幾個定時器的回調(diào)抹蚀。

很多講述定時器的技術(shù)文中都有這么一個觀點,如果一個定時器錯過了本次可以觸發(fā)的時間點企垦,那么定時器將跳過這個時間點环壤,等待下一個時間點的到來,這個觀點似乎是從官方文檔中得來的钞诡,但這個觀點跟定時器在RunLoop中的工作原理并不符郑现。定時消息從內(nèi)核發(fā)出,消息在消息中心等待被處理荧降,RunLoop每次Loop都會去消息中心查找相應的端口消息接箫,若找到相應的端口消息就會進行處理,所以朵诫,即使當前RunLoop正在執(zhí)行一個耗時很長的任務(wù)辛友,當任務(wù)執(zhí)行完進入下一次Loop時,那些未被處理的消息仍然會被處理剪返。經(jīng)過大量測試表明废累,定時消息并不會因延遲而掉失。

關(guān)于RunLoop随夸,官方文檔在這一部份的勘誤比較多九默,經(jīng)常會出現(xiàn)文檔的介紹跟源碼不同的情況,所以想學習RunLoop的同學宾毒,建議看源碼和自己做測試驼修,特別是自己做測試殿遂。

NSTimer和CADisplayLink最大的區(qū)別在于信號的發(fā)射頻率不同,CADisplayLink的發(fā)射頻率固定在16.67ms一次乙各,而NSTimer則可以自由定義墨礁。我在頁面間跳轉(zhuǎn)的性能優(yōu)化(一)中曾經(jīng)提到過,不是必要的情況下耳峦,都不要選擇使用CADisplayLink作為定時器恩静,因為它會使目標RunLoop一直處理活躍狀態(tài)。下面通過一個例子來看看實際的效果蹲坷,創(chuàng)建一個CADisplayLink定時器驶乾,設(shè)置為100秒后觸發(fā),然后觀察目標RunLoop的狀態(tài)循签。

CADisplayLink 從實際效果我們可以看到级乐,目標RunLoop一直處于活躍狀態(tài),不斷地處理內(nèi)核發(fā)出的信號县匠,直到RunLoop Stop或CADisplayLink定時器被移除风科。同樣的條件,我們把定時器換成NSTimer來觀察實際情況乞旦。

NSTimer 與CADisplayLink的固定信號不同贼穆,NSTimer的信號間隔完全是由使用者來定義。所以兰粉,除非你需要實現(xiàn)定時動畫故痊,不然都不要選擇使用CADisplayLink作為定時器,它不僅會損耗大量的CPU資源亲桦,還會響應目標RunLoop處理其它事件源崖蜜。

改動

前面介紹了定時器的工作原理浊仆,現(xiàn)在來看看實際的改動客峭,從一個例子入手進行講述。現(xiàn)在有頁面A抡柿,B舔琅,頁面A,B各有一個按鈕洲劣,頁面A的按鈕用來進入頁面B备蚓,進入頁面B后創(chuàng)建一個子線程,然后向子線程添加一個定時器并啟動RunLoop囱稽,頁面B的按鈕用于停止定時器郊尝,并返回頁面A,頁面B被釋放時會在dealloc方法里輸出dealloc战惊,編譯環(huán)境是ARC流昏,下圖為頁面B的代碼,Gif圖分別是iOS10與iOS9的實際運行效果。

頁面B代碼

iOS10

iOS9 一般情況下况凉,從頁面B返回到頁面A后谚鄙,頁面B會被釋放,頁面B的dealloc方法會輸出dealloc刁绒,但從實際的運行效果可以看到闷营,在iOS10環(huán)境下頁面B并沒有被釋放,WTF知市,為什么iOS10環(huán)境下會這樣傻盟?要回答這個問題,我們需要先知道iOS10的改動是什么嫂丙。

若目標RunLoop當前沒有定時源需要處理(像上面的例子那樣莫杈,子線程RunLoop只有一個定時器,該定時器移除后奢入,則子線程RunLoop沒有定時源需要處理)筝闹,則通知內(nèi)核不需要再向當前Timer Port發(fā)送定時消息并移除該Timer Port。在iOS10環(huán)境下腥光,當移除Timer Port后关顷,內(nèi)核會把消息列表中與該Timer Port相應的定時消息移除,而iOS10以前的環(huán)境下武福,當移除Timer Port后议双,內(nèi)核不會把消息列表中與該Timer Port相應的定時消息移除。iOS10的處理是更為合理的捉片,iOS10以前的處理可能是歷史遺留問題吧平痰。

看回上面的例子,例子中遇到的問題是頁面B返回后并沒有被釋放伍纫,即頁面B的內(nèi)存被強制保留了宗雇,所以我們現(xiàn)在需要知道的是頁面B為什么被強制保留了。在頁面B中我們創(chuàng)建了一個子線程莹规,子線程的主函數(shù)是頁面B的對象函數(shù)赔蒲,這可能是導致頁面B被強制保留的原因,所以良漱,我們需要知道子線程開啟前后舞虱,頁面B對象的引用計數(shù)是否有增加。

創(chuàng)建并開啟子線程

頁面B的引用計數(shù) 從輸出的信息我們得知母市,創(chuàng)建子線程后矾兜,Target會被強制保留,直到子線程的主函數(shù)返回患久。引用計數(shù)在很多時候可以幫助我們了解內(nèi)存的使用情況椅寺,但在ARC編譯環(huán)境下舶沿,我們無法直接使用retainCount方法來獲取一個對象的引用計數(shù),所以配并,我們需要做額外的處理括荡。

獲取對象的引用計數(shù) 回到例子中,我們知道了頁面B被強制保留的原因后溉旋,就知道了怎么解決畸冲,只需要退出子線程即可,子線程之所以可以一直存活观腊,是因為啟動了RunLoop邑闲,所以,我們只需要退出RunLoop梧油,子線程的主函數(shù)就會返回苫耸。例子中涉及到線程異步的問題,定時器是在子線程RunLoop中注冊的儡陨,但定時器的移除操作卻是在主線程褪子,由于子線程RunLoop處理完一次定時信號后,就會進入休眠狀態(tài)骗村。在iOS10以前的環(huán)境下嫌褪,定時器被移除后,內(nèi)核仍然會向?qū)腡imer Port發(fā)送一次信號胚股,所以子線程RunLoop接收到信號后會被喚醒笼痛,由于沒有定時源需要處理,所以RunLoop會直接跳轉(zhuǎn)到判斷階段琅拌,判斷階段會檢測當前RunLoopMode是否有事件源需要處理缨伊,若沒有事件源需要處理,則會退出RunLoop进宝。由于例子中子線程RunLoop的當前RunLoopMode只有一個定時器刻坊,而定時器被移除后,RunLoopMode就沒有了需要處理的事件源即彪,所以會退出RunLoop紧唱,子線程的主函數(shù)也因此返回活尊,頁面B對象被釋放隶校。

但在iOS10環(huán)境下,當定時器被移除后蛹锰,內(nèi)核不再向?qū)腡imer Port發(fā)送任何信號深胳,所以子線程RunLoop一直處于休眠狀態(tài)并沒有退出,而我們只需要手動喚醒RunLoop即可铜犬。

更改頁面B代碼

iOS10 例子中所遇到的問題已經(jīng)解決轻庆,但看完這個例子敛劝,可能你會有疑問,這個例子講述的情況有實戰(zhàn)意義夸盟?這個例子是從一個國外成熟產(chǎn)品所提供的配套SDK中簡化而來,配套的SDK用于與產(chǎn)品進行對接上陕。額......實話說桩砰,當我看到這個處理方式的時候释簿,我被震驚了,沒想到一個成熟產(chǎn)品所提供的配套SDK會出現(xiàn)這樣的問題庶溶,讓我更震驚的是煮纵,隨后在其它SDK中也發(fā)現(xiàn)了這個問題,這......

我們回頭來看看例子中的處理方式偏螺,例子中醉途,子線程RunLoop的退出依賴于RunLoopMode的事件源為空,這種RunLoop的退出方式是極不穩(wěn)定的砖茸,因為系統(tǒng)有很多API會向目標RunLoopMode添加額外的事件源來處理系統(tǒng)事件的隘擎,所以這種方式是不能確保一定可以退出RunLoop的。正確的方式應該是配對調(diào)用CFRunLoopRun( )凉夯,CFRunLoopStop( )來啟動和退出RunLoop货葬,需要注意的是,除非你要創(chuàng)建一個單例線程劲够,不然不要使用[runloop run]方法來啟動RunLoop震桶,因為使用run方法啟動RunLoop后,唯一退出RunLoop的方式是當前RunLoopMode的事件源為空征绎,而我們知道這種方式本身是極不穩(wěn)定的蹲姐。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市人柿,隨后出現(xiàn)的幾起案子柴墩,更是在濱河造成了極大的恐慌,老刑警劉巖凫岖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件江咳,死亡現(xiàn)場離奇詭異,居然都是意外死亡哥放,警方通過查閱死者的電腦和手機歼指,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門爹土,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人踩身,你說我怎么就攤上這事胀茵。” “怎么了挟阻?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵宰掉,是天一觀的道長。 經(jīng)常有香客問我赁濒,道長轨奄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任拒炎,我火速辦了婚禮挪拟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘击你。我一直安慰自己玉组,他們只是感情好,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布惯雳。 她就那樣靜靜地躺著石景,像睡著了一般拙吉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上筷黔,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天佛舱,我揣著相機與錄音,去河邊找鬼订歪。 笑死损拢,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的掏秩。 我是一名探鬼主播荆姆,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼邮破!你這毒婦竟也來了仆救?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤摧莽,失蹤者是張志新(化名)和其女友劉穎镊辕,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體征懈,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡卖哎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年棉饶,在試婚紗的時候發(fā)現(xiàn)自己被綠了镇匀。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡幸缕,死狀恐怖晰韵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情栏尚,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布译仗,位于F島的核電站,受9級特大地震影響纵菌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜笛辟,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一序苏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧围来,春花似錦踱阿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鸳玩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間不跟,已是汗流浹背米碰。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吕座,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓漆诽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親兰英。 傳聞我的和親對象是個殘疾皇子蚪腐,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

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

  • 前言 iOS10已經(jīng)發(fā)布了一段時間回季,iOS10的各種適配相信大家已經(jīng)完成正林。本文將講述的是關(guān)于iOS10內(nèi)核的一...
    Delpan閱讀 3,788評論 24 37
  • 之前要做一個發(fā)送短信驗證碼的倒計時功能,打算用NSTimer來實現(xiàn)鼻忠,做的過程中發(fā)現(xiàn)坑還是有不少的杈绸。 基本使用 NS...
    WeiHing閱讀 4,385評論 1 8
  • 在軟件開發(fā)過程中瞳脓,我們常常需要在某個時間后執(zhí)行某個方法,或者是按照某個周期一直執(zhí)行某個方法埋酬。在這個時候写妥,我們就需要...
    誰遇而安閱讀 20,932評論 2 20
  • 李雪蘭 光陰似箭审姓,歲月如梭。40多年前扎筒,來自合陽縣的各公社、各村莊的莘莘學子砸琅,躊躇滿志轴踱,憑借優(yōu)異的學習成績和個人...
    PatrickBJZP閱讀 931評論 0 0
  • 今天我和孩子互相表揚了對方。先說一下我對孩子的表揚诱篷,今天上午于老師打電話給我,孩子肚子疼闸盔,我趕快騎車把他領(lǐng)回...
    昊天媽媽閱讀 293評論 1 2