剖析Objective-C內(nèi)存管理規(guī)則
詳解Objective-C 2.0 關(guān)于Objective-C內(nèi)存管理規(guī)則是本文要介紹的內(nèi)容,不多說,先來看內(nèi)容。Objective-C 2.0增加了一些新的東西,包括屬性和垃圾回收。那么,我們在學(xué)習(xí)Objective-C 2.0之前,最好應(yīng)該先了解,從前是什么樣的,為什么Objective-C 2.0要增加這些支持。
這一切都跟Cocoa內(nèi)存的管理規(guī)則有關(guān)系,我們知道,Objective-C中所有變量都定義為指針。指針是一個特殊的變量,它里面存儲的數(shù)值被解釋成為內(nèi)存里的一個地址,如果使用不當(dāng),就會出錯或者造成內(nèi)存的泄露。要了解這些,就需要看看其內(nèi)存管理的規(guī)則到底是什么樣的。
這篇文章也應(yīng)該做為蘋果開發(fā)工具中提供的性能調(diào)試工具Instruments使用前必讀知識進(jìn)行閱讀。要知道,如果你使用Objective-C 2.0,那么本文描述的大部分工作你都不需要自己去處理了。但是這并不意味著你可以不了解它,相反,只有你對內(nèi)存管理規(guī)則更加了解,你才能更好地使用Objective-C 2.0帶來的便利。
當(dāng)Cocoa新手在進(jìn)行內(nèi)存管理時,他們看上去總是把事情變得更為復(fù)雜。遵循幾個簡單的規(guī)則就可以把生活變得更簡單。而不遵循這些規(guī)則,他們幾乎一定會造成諸如內(nèi)存泄露或者將消息發(fā)送給釋放掉的對象而出現(xiàn)的的運(yùn)行錯誤。
Cocoa不使用垃圾回收(當(dāng)然,Objective-C 2.0之后開始就使用了),你必須通過計算reference的數(shù)量進(jìn)行自己的內(nèi)存管理,使用-retain, -release和-autorelease。
方法描述
retain
將一個對象的reference數(shù)量增加1。
release
將一個對象的reference數(shù)量減少1。
autorelease
在未來某些時候?qū)eference數(shù)量減少1.
alloc
為一個對象分配內(nèi)存,并設(shè)置保留值數(shù)量(retain count)為1。
copy
復(fù)制一個對象,并將其做為返回值。同時設(shè)置保留值數(shù)量(retain count)為1。
保留值數(shù)量規(guī)則
1、在一定的代碼段中,使用-copy,-alloc和-retain的次數(shù)應(yīng)該和-release,-autorelease保持一致。
2、使用便利構(gòu)造方法創(chuàng)建的對象(比如NSString的stringWithString)可以被認(rèn)為會被自動釋放。(autoreleased)
3、在使用你自己的參數(shù)實(shí)例時,需要實(shí)現(xiàn)-dealloc方法來釋放。
例子
- -alloc / -release
- - (void)printHello
- {
- NSString *string;
- string = [[NSString alloc] initWithString:@"Hello"];
- NSLog(string);
- // 我們用 alloc 創(chuàng)建了NSString,那么需要釋放它
- [string release];
- }
便利構(gòu)造方法
- (void)printHello
- NSString *string;
- string = [NSString stringWithFormat:@"Hello"];
- NSLog(string);
- // 我們用便利構(gòu)造方法創(chuàng)建的NSString
- //我們可以認(rèn)為它會被自動釋放
永遠(yuǎn)使用存取方法
雖然有時候你可能會認(rèn)為這很麻煩,但是如果你始終使用了存取方法,造成內(nèi)存管理問題的麻煩將會降低很多。
如果你在代碼實(shí)例的參數(shù)中頻繁使用-retain和-release,幾乎可以肯定你做了錯誤的事情。
例子
假設(shè)我們希望設(shè)置一個Counter對象的數(shù)量值。
- @interface Counter : NSObject
- {
- NSNumber *count;
- }
為了獲取和設(shè)置count值,我們定義兩個存取方法:
- - (NSNumber *)count
- {
- return count;
- // 無需retain或者release,
- // 僅僅傳遞數(shù)值
- }
- - (void)setCount:(NSNumber *)newCount
- {
- // newCount值會被自動釋放,那么我們希望保留這個newCount
- // 所以需要在這里retain。
- [newCount retain];
- // 由于我們在這個方法中僅僅改變了計算數(shù)量的對象,我們可以在這里先釋放它。因?yàn)閇nil release]在objective-c中也是允許的,所以即使count值沒有被指定,也可以這樣調(diào)用。
- //我們必須在[newCount retain]之后再釋放count,因?yàn)橛锌赡苓@兩個對象的指針是同一個。我們不希望不小心釋放它。
- [count release];
- // 重新指定
- count = newCount;
- }
命名約定,注意存取方法的命名約定遵循一個模式: -參數(shù)名 和 -set參數(shù)名。
遵循這一約定,會使你的代碼可讀性更強(qiáng),而且,更重要地是你可以在后面使用key-value編碼。(參閱NSKeyValueCoding協(xié)議)。
由于我們有一個對象實(shí)例參數(shù),我們必須實(shí)現(xiàn)一個釋放方法:
- (void)dealloc
- {
- [self setCount:nil];
- [super dealloc];
- }
假設(shè)我們希望實(shí)現(xiàn)一個方法重置計數(shù)器,我們會有很多選擇。在最開始,我們使用了一個 便利構(gòu)造方法,所以我們假設(shè)新的數(shù)值是自動釋放的。我們不需要發(fā)送任何retain或者release消息。
- (void)reset
- {
- NSNumber *zero = [NSNumber numberWithInt:0];
- [self setCount:zero];
- }
然而,如果我們使用-alloc方法建立的NSNumber實(shí)例,那我們必須同時使用一個-release。
- (void)reset
- NSNumber *zero = [[NSNumber alloc] initWithInt:0];
- [self setCount:zero];
- [zero release];
常見錯誤
在簡單的情況下,以下代碼幾乎一定可以正常運(yùn)行,但是由于可能沒有使用存取方法,下面的代碼在某些情況下幾乎一定會出問題。
錯誤-沒有使用存取方法
- (void)reset
- {
- NSNumber *zero = [[NSNumber alloc] initWithInt:0];
- [count release]
- count = zero;
- }
錯誤-實(shí)例泄露
- (void)reset
- {
- NSNumber *zero = [[NSNumber alloc] initWithInt:0];
- [self setCount:zero];
- }
新建的NSNumber數(shù)值數(shù)量是1(通過alloc),而我們在這個方法里沒有發(fā)出-release消息。那么這個NSNumber就永遠(yuǎn)不會被釋放了,這樣就會造成內(nèi)存泄露。
錯誤-對已經(jīng)釋放的實(shí)例發(fā)送-release消息
- - (void)reset
- {
- NSNumber *zero = [NSNumber numberWithInt:0];
- [self setCount:zero];
- [zero release];
- }
你隨后在存取count的時候在這里就會出錯。這個簡便構(gòu)造方法會返回一個自動釋放的對象,你無需發(fā)送其他釋放消息。
這樣寫代碼意味著,由于對象已經(jīng)被自動釋放,那么當(dāng)你釋放時,retain count將被減至0,對象已經(jīng)不存在了。當(dāng)你下次希望獲取count值時,你的消息會發(fā)到一個不存在的對象(通常這樣你會得到一個SIGBUS 10的錯誤提示)。
經(jīng)常造成混淆的情況
數(shù)組和其他集合類
當(dāng)對象被加入到數(shù)組、字典或者集合中,集合類會將其保留。當(dāng)集合被釋放的同時,對象也會收到一個釋放消息。如果你希望寫一個建立數(shù)字?jǐn)?shù)組的例子,你可能會這么寫:
- NSMutableArray *array;
- int i;
- // …
- for (i = 0; i < 10; i++)
- {
- NSNumber *n = [NSNumber numberWithInt: i];
- [array addObject: n];
- }
在這個例子里,你無需保留新建的數(shù)值,因?yàn)閿?shù)組會幫你保留。
- NSMutableArray *array;
- int i;
- // …
- for (i = 0; i < 10; i++)
- {
- NSNumber *n = [[NSNumber alloc] initWithInt: i];
- [array addObject: n];
- [n release];
- }
本例中,在for循環(huán)里你需要給n發(fā)送一個-release消息,因?yàn)槟阈枰冀K在-alloc之后將n的數(shù)量保持為1。這么做的原因是當(dāng)其通過-addObject:方法被添加至數(shù)組中時,數(shù)組已經(jīng)將其保存起來。即使你釋放了n,但是這個數(shù)字由于已經(jīng)保存在數(shù)組里,所以不會被釋放。
為了了解這些,假設(shè)你自己就是編寫數(shù)組類的人。你不希望接收的對象未經(jīng)你同意就消失,所以你會在對象傳遞進(jìn)來時,對其發(fā)送一個-retain消息。如果他們被刪除,你同時也要對應(yīng)地發(fā)送一個-release消息。在你自己-dealloc時,你也要給你收到的所有對象發(fā)送一個-release。
小結(jié):詳解Objective-C 2.0 關(guān)于Objective-C內(nèi)存管理規(guī)則的內(nèi)容介紹完了,希望本文對你有所幫助。