iOS容易循環(huán)引用的三種場景(轉(zhuǎn))

ARC已經(jīng)出來很久了,自動釋放內(nèi)存的確很方便,但是并非絕對安全絕對不會產(chǎn)生內(nèi)存泄露。導(dǎo)致iOS對象無法按預(yù)期釋放的一個無形殺手是——循環(huán)引用。循環(huán)引用可以簡單理解為A引用了B,而B又引用了A,雙方都同時保持對方的一個引用,導(dǎo)致任何時候引用計數(shù)都不為0,始終無法釋放。若當(dāng)前對象是一個ViewController,則在dismiss或者pop之后其dealloc無法被調(diào)用,在頻繁的push或者present之后內(nèi)存暴增,然后APP就duang地掛了。下面列舉我們變成中比較容易碰到的三種循環(huán)引用的情形。


(1)計時器NSTimer

一方面,NSTimer經(jīng)常會被作為某個類的成員變量,而NSTimer初始化時要指定self為target,容易造成循環(huán)引用。 另一方面,若timer一直處于validate的狀態(tài),則其引用計數(shù)將始終大于0。先看一段NSTimer使用的例子(ARC模式):

我們所期待的結(jié)果是,初始化5秒后,f對象被release,f的dealloc方法被調(diào)用,在dealloc里面timer失效,對象被析構(gòu)。但結(jié)果卻是如此:


這是為什么呢?主要是因?yàn)閺膖imer的角度,timer認(rèn)為調(diào)用方(Friend對象)被析構(gòu)時會進(jìn)入dealloc,在dealloc可以順便將timer的計時停掉并且釋放內(nèi)存;但是從Friend的角度,他認(rèn)為timer不停止計時不析構(gòu),那我永遠(yuǎn)沒機(jī)會進(jìn)入dealloc。循環(huán)引用,互相等待,子子孫孫無窮盡也。問題的癥結(jié)在于-(void)cleanTimer函數(shù)的調(diào)用時機(jī)不對,顯然不能想當(dāng)然地放在調(diào)用者的dealloc中。一個比較好的解決方法是開放這個函數(shù),讓Friend的調(diào)用者顯式地調(diào)用來清理現(xiàn)場。如下:


(2)block

block在copy時都會對block內(nèi)部用到的對象進(jìn)行強(qiáng)引用(ARC)或者retainCount增1(非ARC)。在ARC與非ARC環(huán)境下對block使用不當(dāng)都會引起循環(huán)引用問題,一般表現(xiàn)為,某個類將block作為自己的屬性變量,然后該類在block的方法體里面又使用了該類本身,簡單說就是self.someBlock = ^(Type var){[self dosomething];或者self.otherVar = XXX;或者_(dá)otherVar = ...};block的這種循環(huán)引用會被編譯器捕捉到并及時提醒。舉例如下,依舊以Friend類為例子:

我們看到,在block的實(shí)現(xiàn)內(nèi)部又使用了Friend類的arr屬性,xcode給出了warning, 運(yùn)行程序之后也證明了Friend對象無法被析構(gòu):

網(wǎng)上大部分帖子都表述為"block里面引用了self導(dǎo)致循環(huán)引用",但事實(shí)真的是如此嗎?我表示懷疑,其實(shí)這種說法是不嚴(yán)謹(jǐn)?shù)?,不一定要顯式地出現(xiàn)"self"字眼才會引起循環(huán)引用。我們改一下代碼,不通過屬性self.arr去訪問arr變量,而是通過實(shí)例變量_arr去訪問,如下:


由此我們知道了,即使在你的block代碼中沒有顯式地出現(xiàn)"self",也會出現(xiàn)循環(huán)引用!只要你在block里用到了self所擁有的東西!但對于這種情況,我們無法通過加__weak聲明或者_(dá)_block聲明去禁止block對self進(jìn)行強(qiáng)引用或者強(qiáng)制增加引用計數(shù)。但我們可以通過其他指針來避免循環(huán)引用(多謝xq_120的提醒),具體是這么做的:

對于self.arr的情況,我們要分兩種環(huán)境去解決:

1)ARC環(huán)境下:ARC環(huán)境下可以通過使用_weak聲明一個代替self的新變量代替原先的self,我們可以命名為weakSelf。通過這種方式告訴block,不要在block內(nèi)部對self進(jìn)行強(qiáng)制strong引用:(如果要兼容ios4.3,則用__unsafe_unretained代替__weak,不過目前基本不需考慮這么low的版本)

2)MRC環(huán)境下:解決方式與上述基本一致,只不過將__weak關(guān)鍵字換成__block即可,這樣的意思是告訴block:小子,不要在內(nèi)部對self進(jìn)行retain了!


(3)委托delegate

在委托問題上出現(xiàn)循環(huán)引用問題已經(jīng)是老生常談了,本文也不再細(xì)講,規(guī)避該問題的殺手锏也是簡單到哭,一字訣:聲明delegate時請用assign(MRC)或者weak(ARC),千萬別手賤玩一下retain或者strong,畢竟這基本逃不掉循環(huán)引用了!


轉(zhuǎn)載于 編程小翁@博客園,郵件zilin_weng@163.com,微信Jilon【原】iOS容易造成循環(huán)引用的三種場景

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

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

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