29.理解引用計數(shù)

《編寫高質(zhì)量iOS與OS X代碼的52個有效方法》--第五章 第29條
(ps:此乃讀書筆記,加深記憶,僅供大家參考)


第5章 內(nèi)存管理

ARC幾乎把所有內(nèi)存管理事宜都交給編譯器決定,開發(fā)者只需專注于業(yè)務邏輯。

第29條:理解引用計數(shù)

Objective-C語言使用引用計數(shù)來管理內(nèi)存,也就是說,每個對象都有個可以遞增或遞減的計數(shù)器。如果想使某個對象繼續(xù)存活,那就遞增其引用計數(shù);用完了之后,就遞減其引用計數(shù)。計數(shù)變?yōu)?,就表示沒人關(guān)注此對象了,于是,就可以把它銷毀。

引用計數(shù)工作原理

在引用計數(shù)架構(gòu)下,對象有個計數(shù)器,用以表示當前有多少個事物想令此對象繼續(xù)存活下去。這在Objective-C中叫做“保留計數(shù)”(retain count),不過也可以叫“引用計數(shù)”(reference count)。NSObject協(xié)議聲明了下面三個方法用于操作計數(shù)器,以遞增或遞減其值:

  • Retain 遞增保留計數(shù)。
  • release 遞減保留計數(shù)。
  • autorelease 待稍后清理“自動釋放池”(autorelease pool)時,再遞減保留計數(shù)。

查看保留計數(shù)的方法叫做retainCount, 此方法不太有用,即便在調(diào)試時也如此,所以筆者(蘋果公司)并不推薦大家使用這個方法。

對象創(chuàng)建出來時,其保留計數(shù)至少為1。 最終當保留計數(shù)歸零時,對象就回收了(deallocated),也就是說,系統(tǒng)會將其占用的內(nèi)存標記為“可重用”(reuse)。此時,所有指向該對象的引用也都變得無效了。

對象如果持有指向其他對象的強引用(strong reference),那么前者就“擁有”(own)后者。也就是說,對象想令其所引用的那些對象繼續(xù)存活,就可將其“保留”。等用完了之后,再釋放。

如果按“引用樹”回溯,那么最終會發(fā)現(xiàn)一個“跟對象”(root object)。在Mac OS X應用程序中,此對象就是NSApplication對象;而在iOS應用程序中,則是UIApplication對象。兩者都是應用程序啟動時創(chuàng)建的單例

NSMutableArray * array = [[NSMutableArray alloc] init]; 
NSNumber * number = [[NSNumber alloc] initWithInt:2333];
[array addObject:number];    
[number release];    
//do something with 'array'    
[array release];

在Objective-C中,調(diào)用alloc方法所返回的對象由調(diào)用者所擁有。也就是說,調(diào)用者已通過alloc方法表達了想令該對象繼續(xù)存活下去的意思。不過請注意,這并不是說此對象此時的保留計數(shù)必定是1。在alloc或“initWitInt:”方法的實現(xiàn)代碼中,也許還有其他對象也保留了此對象,所以,其保留計數(shù)至少為1。不能說保留計數(shù)一定是某個值,只能說你所執(zhí)行的操作是遞增了該計數(shù)還是遞減了該計數(shù)。

number對象調(diào)用release釋放之后,仍然存活.因為數(shù)組還在引用著它。然而絕不應該假設此對象一定存活,也就是說,不要像下面這樣編寫代碼:

NSNumber * number = [[NSNumber alloc] initWithInt:2333];
[array addObject:number];
[number release];
NSLog(@"number = %@", number);

如果調(diào)用release由于某些原因,其保留計數(shù)降至0,那么number對象所占內(nèi)存也許會回收,這樣的話,在調(diào)用NSLog可能就將是程序崩潰了。這里說“可能”,而沒說“一定”,因為對象所占的內(nèi)存在“解除分配”(deallocated)之后,只是放回“可用內(nèi)存池”(avaiable pool)。如果執(zhí)行NSLog時尚未復寫對象內(nèi)存,那么該對象仍然有效,這時程序不會崩潰。

為避免在不經(jīng)意間使用了無效對象,一般調(diào)用完release之后都會清空指針。這就能保證不會出現(xiàn)可能指向無效對象的指針,這種指針通常稱為“懸掛指針”(dangling pointer)。

NSNumber * number = [[NSNumber alloc] initWithInt:2333];
[array addObject:number];
[number release];
number = nil;

屬性存取方法中的內(nèi)存管理

若屬性為“strong關(guān)系”(strong relationship),則設置的屬性值會保留。

- (void)setFoo:(id)foo
{
    [foo retain];
    [_foo release];
    _foo = foo;
}   

此方法將保留新值并釋放舊值,然后更新實例變量,令其指向新值。順序很重要。

自動釋放池

調(diào)用release會立刻遞減對象的保留計數(shù)(而且還有可能令系統(tǒng)回收此對象),然而有時候可以不調(diào)用它,改為調(diào)用autorelease,此方法會在稍后遞減計數(shù),通常是在下一次“時間循環(huán)”(event loop)時遞減,不過也可能執(zhí)行的更早些。

此特性很有用,尤其是在方法中返回對象時更應該使用它。在這種情況下,我們并不總是想令方法調(diào)用者手工保留其值。

- (NSString *)stringValue
{
    NSString * str = [[NSString alloc] initWithFormat:@"I am this: %@", self];
    return self;    //return [str autorelease];
}

此時返回的str對象其保留計數(shù)比期望值要多1(+1 retain count),因為調(diào)用alloc會令保留計數(shù)加1,而又沒有與之對應的釋放操作。

這里應該用autorelease,它會在稍后釋放對象,從而給調(diào)用者留下了足夠長的時間,使其可以在需要時先保留返回值。換句話說,此方法可以保證對象在跨越“方法調(diào)用邊界”(method call boundary)后一定存活。

保留環(huán)

通常采用“弱引用”(weak reference)來解決此問題,或是從外界命令循環(huán)中的某個對象不再保留另一個對象。這兩種方法都能打破保留環(huán),從而避免內(nèi)存泄露。

要點

  • 引用計數(shù)機制通過可以遞增遞減的計數(shù)器來管理內(nèi)存。對象創(chuàng)建好之后,其保留計數(shù)至少為1。若保留計數(shù)為正,則對象繼續(xù)存活。當保留計數(shù)降為0時,對象就被銷毀了。
  • 在對象生命期中,其余對象通過引用來保留或釋放此對象。保留與釋放操作分別會遞增或遞減保留計數(shù)。

理解 iOS 的內(nèi)存管理----唐巧大神

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

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

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