oc編程中為了處理對(duì)象,可將變量類型定義為id類型或各種對(duì)象類型。
所謂對(duì)象類型就是指向NSObject這樣的oc類的指針,例如“NSObject *”。id類型用于隱藏對(duì)象類型的類名部分。相當(dāng)于C語言中常用的“void *”;
ARC有效時(shí),id類型和對(duì)象類型同C語言類型不同,其類型上必須加所有權(quán)修飾符,所有權(quán)修飾符一共有四種。
__strong,
__weak,
__unsafe_unretained,
__autoreleasing
__strong 修飾符
__strong 修飾符是id類型和對(duì)象類型默認(rèn)的所有權(quán)修飾符,也就是說,以下源代碼中的id變量,實(shí)際上被添加了所有權(quán)修飾符。
id objc = [[NSObject alloc] init];
id和對(duì)象類型在沒有明確指定修飾符的時(shí)候,默認(rèn)添加__strong修飾符,上面的源碼與以下相同。
id __strong objc = [[NSObject alloc] init];
那么在MRC下它是如何表達(dá)的呢?
id objc = [[NSObject alloc] init];
[objc release];
為了釋放生成并持有的對(duì)象,增加了release方法的代碼。該源代碼進(jìn)行的動(dòng)作同原先ARC的動(dòng)作完全一樣。
如代碼所示,objc在超出它的變量作用域時(shí),即在該變量被廢棄的時(shí)候,會(huì)釋放它被賦予的對(duì)象。(PS:__strong修飾符不僅只在變量作用域中,在賦值和作為類成員變量上也能夠正確地管理其對(duì)象的所有者,感興趣的讀者可以自己嘗試去編寫)。
正如蘋果宣稱的那樣,通過__strong修飾符,不必再次鍵入retain或者release,完美的滿足了引用計(jì)數(shù)內(nèi)存管理的思考方式。
· 自己生成的對(duì)象,自己所持有。
· 非自己生成的對(duì)象,自己也能持有。
· 不再需要自己持有的對(duì)象時(shí)釋放。
· 非自己持有的對(duì)象無法釋放。
__weak修飾符
看起來__strong修飾符完美的解決了內(nèi)存管理問題,但是僅僅通過__strong修飾符是完全不夠的。這里提到的問題就是“循環(huán)引用”或者“自引用的問題”。如下圖。

我們也可以通過代碼來演示
@interface Test : NSObject
{
??????? id __strong _obj;
}
- (void)setObject:(id __strong)obj;
@end
@implementation Test
- (void)setObject:(id)obj{
_obj = obj;
}
@end
{
id test0 = [[Test alloc] init];//對(duì)象A
id test1 = [[Test alloc] init];//對(duì)象B
[test0 setObject:test1];
[test1 setObject:test0];
}
當(dāng)出了作用域之后,test0和test1分別釋放,但是持有對(duì)象A的強(qiáng)引用變?yōu)閷?duì)象B的_obj,持有對(duì)象B的強(qiáng)引用變?yōu)閷?duì)象A的_obj,從而造成了內(nèi)存泄露(內(nèi)存空間使用完畢之后未回收)。
像下面這種情況,雖然只有一個(gè)對(duì)象,但是該對(duì)象對(duì)其自身持有,會(huì)發(fā)生自引用
id test0 = [[Test alloc] init];
[test0 setObject:test0];

那么我們?cè)鯓硬拍鼙苊膺@些情況呢?就是我們所說的__weak修飾符,提供弱引用,它不會(huì)持有對(duì)象,等對(duì)象的持有者不存在時(shí),會(huì)自動(dòng)廢棄weak的對(duì)象,我們來看下面的代碼。
id __weak objc = [[Test alloc] init];
這段代碼編譯器并不會(huì)報(bào)錯(cuò)只是會(huì)給個(gè)警告。這段代碼將自己生成并持有的對(duì)象賦值給了帶有__weak修飾符的變量objc。即變量objc持有對(duì)持有對(duì)象的弱引用。因此,為了不以自己持有的狀態(tài)來保存自己生成并持有的對(duì)象,生成的對(duì)象會(huì)被立即釋放。我們用下面的代碼解決上面的警告。
id __strong objc = [[Test alloc] init];
id __weak weakObj = objc;
像這樣,__weak修飾符可以避免循環(huán)引用的問題,通過檢查__weak修飾的變量是否為空,可以判斷被賦值的對(duì)象是否已經(jīng)廢棄。(在ios4之前,我們可以用__unsafe_unretained來代替)。
__unsafe_unretained修飾符
__unsafe_unretained修飾符,是不安全的所有權(quán)修飾符。盡管ARC的內(nèi)存管理是編譯器的工作,但是賦有這個(gè)修飾符的變量不屬于編譯器的內(nèi)存管理對(duì)象。這一點(diǎn)讀者需要注意。
id __unsafe_unretained objc = [[Test alloc] init];
賦有該修飾符的變量和__weak修飾符一樣,因?yàn)樽约荷刹⒊钟械膶?duì)象不能繼續(xù)為自己所有,所以生成的對(duì)象會(huì)被立即釋放。但是他們的區(qū)別是什么呢?想想為什么我們需要使用到它。比如在ios4以前。賦值給賦有__unsafe_unretained修飾符變量的對(duì)象在使用的時(shí)候,如果沒有確保其真實(shí)存在,那么應(yīng)用程序就會(huì)崩潰。而賦有__weak修飾符的變量則會(huì)輸出nil。
__autoreleasing修飾符
ARC有效的時(shí)候我們是不能使用autorelease方法的,另外,也不能使用NSAutoreleasePool類,這樣一來,雖然autorelease無法使用,但是ARC下autorelease功能是有效的。如下代碼
@autoreleasepool {
????? id __autoreleasing obj = [[Test alloc] init];
}
我們可以看到“@autoreleasepool”塊代替了原來的NSAutoreleasePool,__autoreleasing代替了原來的 autorelease方法,即對(duì)象被注冊(cè)到了自動(dòng)釋放池。但是顯示的加__autoreleasing修飾符在ARC似乎沒有那么常見。
取得非自己生成并持有對(duì)象時(shí),雖然可以使用alloc/new/copy/mutableCopy以外的方法來取得對(duì)象,但該對(duì)象已經(jīng)被注冊(cè)到自動(dòng)釋放池了。這同在MRC時(shí)取得調(diào)用了autorelease方法的對(duì)象是一樣的。這是由于編譯器會(huì)檢查方法名是否以alloc/new/copy/mutableCopy開始,如果不是則自動(dòng)將返回值的對(duì)象注冊(cè)到自動(dòng)釋放池里面。我們看下面的代碼。
@autoreleasepool {
id __strong obj = [NSMutableArray array];
}
我們可以看到不使用__autoreleasing也能將obj注冊(cè)到自動(dòng)釋放池,這是為什么呢?原因就像我上面講到的,編譯器會(huì)自動(dòng)的查看方法名是否以alloc/new/copy/mutableCopy開始。我們可以看看array方法里面做了什么。
+ (id)array{
return [[NSMutableArray alloc] init];
}
當(dāng)然我們也可以這么寫:
+ (id)array{
id __strong obj = [[NSMutableArray alloc] init];
return obj;
}
我們來分析一下,由于return對(duì)象的時(shí)候出了變量的作用域,所以該強(qiáng)引用的對(duì)象會(huì)被釋放,但是作為函數(shù)的返回值,編譯器會(huì)自動(dòng)將其注冊(cè)到自動(dòng)釋放池。