引用計數(shù)基本概念
當創(chuàng)建一個新對象的時候,引用計數(shù)加1,當某個指針不再指向這個對象時,引用計數(shù)減1,當對象的引用計數(shù)為0時,系統(tǒng)自動將對象銷毀,回收內(nèi)存。
引用《Pro Multithreading and Memory Management for iOS and OS X》中的圖片
我們?yōu)槭裁葱枰糜嫈?shù)
ARC模式下,在同一個函數(shù)內(nèi),我們通常不需要手動處理引用計數(shù),此時的使用場景比較簡單。引用計數(shù)真正發(fā)揮作用的場景在面向?qū)ο蟮某绦蛟O(shè)計架構(gòu)中,用于對象之間的傳遞和共享數(shù)據(jù)。
例如:對象A生成對象M,調(diào)用對象B的一個方法將M作為參數(shù)傳遞。根據(jù)(誰申請誰釋放)原則,應(yīng)該由A在B不需要M的時候來銷毀它。
那么問題來了,B何時才不需要M是不確定的。
Method 1:非常暴力的在A調(diào)用完對象B之后立刻銷毀對象M,那么對象B就很懵逼的需要將參數(shù)另外復(fù)制一份M2,自己來管理對象M2的生命周期。
這樣有一個很大的問題:大量的內(nèi)存申請,復(fù)制,釋放操作,實在太影響性能。
Method 2:使用引用計數(shù),哪些對象需要長時間使用M,就把它的引用計數(shù)加1,用完之后再把引用計數(shù)減1,所有對象都遵循這個原則,生命周期管理的重任就交給引用計數(shù)了。
不要向已經(jīng)釋放的對象發(fā)送消息
手動release一個對象,打印其引用計數(shù)為什么不是0?
理論上說,一個被release的對象的引用計數(shù)值是不確定的,而且如果該對象所占有的內(nèi)存已經(jīng)被復(fù)用,那么打印其引用計數(shù)值程序可能會異常崩潰。
當系統(tǒng)確定一個對象要被release后,就沒有必要對它進行引用計數(shù)減1了。因為肯定會被回收,回收后,它所擁有的內(nèi)存區(qū)域,包括retainCount值都變得沒有意義。
不進行減1可以減少內(nèi)存操作,加速對象回收。
Perfect But 問題又來了
reference cycles 循環(huán)引用
這也是ARC的一個痛點,對象A與對象B如果互相持有,就會造成循環(huán)引用,即使沒有任何指針訪問它們,也不會被釋放。這樣的情況也多發(fā)生在多個對象互相持有,block的使用 NSTimer等。
Method 1:明確會造成循環(huán)引用的位置,主動斷開其中一環(huán)。
Method 2:使用weak reference(弱引用)
持有對象,但不增加引用計數(shù),避免循環(huán)應(yīng)用的產(chǎn)生。比如delegate通常是弱引用的。
關(guān)于block中的循環(huán)引用
在block中的循環(huán)引用一般會被編譯器所提醒,但并不是使用到self才會造成循環(huán)引用,只要用到self所擁有的東西都會。需要使用其它指針來避免block對自身強引用,如下:
__weak typeof(self) weakSelf = self;
self.blkA = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;//加一下強引用,避免weakSelf被釋放掉
NSLog(@"%@", strongSelf->_xxView); //不會導致循環(huán)引用.
};
這是針對ARC的方式,MRC下要用__block
通常情況下,GCD 中的block并不會出現(xiàn)針對self循環(huán)引用的情況,因為self并沒有對GCD中的block進行持有。
參考資料
1.《iOS 開發(fā)進階》 唐巧
2.iOS中block的循環(huán)引用問題 - 簡書