NSTimer內(nèi)存/循環(huán)引用問題整合

日常開發(fā)最常用的定時(shí)方法還是NSTimer,CADisplayLink跟GCDTimer寫起來(lái)還是略麻煩。但是對(duì)于重復(fù)的任務(wù)有一個(gè)重要問題:何時(shí)停掉這個(gè)任務(wù)。而且NSTimer經(jīng)常會(huì)出現(xiàn)循環(huán)引用的問題,因此整合記錄下。

情況一:

Timer會(huì)強(qiáng)持有target,所以如果我們寫個(gè)vc持有一個(gè)Timer,并且這個(gè)Timer初始化以vc為target,那就循環(huán)引用了


情況一
結(jié)果:
循環(huán)情況:

這種情況下即便我們pop了TestVC,testvc dealloc依然不會(huì)出現(xiàn),控制臺(tái)會(huì)無(wú)限制打印a

解決方法:

合適的情況下調(diào)用timer.invalidate()手動(dòng)停掉timer,打破循環(huán):



image.png

情況二:

那如果我們不持有這個(gè)timer,在某個(gè)方法里timer只是作為一個(gè)局部變量來(lái)一vc為target,這樣testvc就不會(huì)搶引用timer,是否可以避免循環(huán)引用?


結(jié)果:

依然未釋放。這是因?yàn)閱?dòng)timer時(shí)需要把timer添加到當(dāng)前runloop中,那當(dāng)前runloop就會(huì)持有timer如果不調(diào)用timer的invalidate方法,這個(gè)timer就無(wú)法被runloop釋放,timer所持有的target也無(wú)法被釋放。(scheduledTimer方法相當(dāng)于initTimer() + fire() + runloop.addTimer()三合一)


image.png
解決方法:

標(biāo)記這個(gè)timer,合適的時(shí)候手動(dòng)停掉

綜合解決方案:在合適的時(shí)候停掉timer

思路:使用中間target:
image.png

使用一個(gè)InnerTarget,初始化Timer時(shí),InnerTarget弱持有testvc,持有selector,弱持有testvc,timer強(qiáng)引用InnerTarget,在InnerTarget的selector中,使用weak target來(lái)調(diào)用selector方法,這樣testvc就可以正常dealloc,當(dāng)testvc釋放時(shí),InnerTarget可以感知,停止Timer即可實(shí)現(xiàn)自動(dòng)釋放timer:


image.png

再試試看:


image.png

image.png

正常釋放
iOS10 新增的block初始化Timer的方法,解決了Timer強(qiáng)引用target的問題,但是無(wú)法解決Runloop持有Timer的問題,仍然需要手動(dòng)釋放timer

嘗試:使用stopTarget+block來(lái)初始化Timer,使用思路類似于Target:

image.png
使用:
image.png

image.png

可以正常釋放

缺點(diǎn):

因?yàn)閎lock會(huì)對(duì)內(nèi)部對(duì)象進(jìn)行一次引用計(jì)數(shù)+1所以如果block里出現(xiàn)了self,請(qǐng)一定要使用weak self。否則依然會(huì)出現(xiàn)循環(huán)引用情況:


image.png
引用圖:
image.png

使用weak/unowned self可打破引用問題:


image.png
引用圖:
image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容