oc編程中為了處理對象裤纹,可將變量類型定義為id類型或各種對象類型。
所謂對象類型就是指向NSObject這樣的oc類的指針丧没,例如“NSObject *”鹰椒。id類型用于隱藏對象類型的類名部分。相當于C語言中常用的“void *”呕童;
ARC有效時漆际,id類型和對象類型同C語言類型不同,其類型上必須加所有權修飾符夺饲,所有權修飾符一共有四種奸汇。
__strong,
__weak,
__unsafe_unretained,
__autoreleasing
__strong 修飾符
__strong 修飾符是id類型和對象類型默認的所有權修飾符,也就是說往声,以下源代碼中的id變量擂找,實際上被添加了所有權修飾符。
id objc = [[NSObject alloc] init];
id和對象類型在沒有明確指定修飾符的時候浩销,默認添加__strong修飾符贯涎,上面的源碼與以下相同。
id __strong objc = [[NSObject alloc] init];
那么在MRC下它是如何表達的呢慢洋?
id objc = [[NSObject alloc] init];
[objc release];
為了釋放生成并持有的對象塘雳,增加了release方法的代碼陆盘。該源代碼進行的動作同原先ARC的動作完全一樣。
如代碼所示败明,objc在超出它的變量作用域時隘马,即在該變量被廢棄的時候,會釋放它被賦予的對象妻顶。(PS:__strong修飾符不僅只在變量作用域中酸员,在賦值和作為類成員變量上也能夠正確地管理其對象的所有者,感興趣的讀者可以自己嘗試去編寫)盈包。
正如蘋果宣稱的那樣沸呐,通過__strong修飾符,不必再次鍵入retain或者release呢燥,完美的滿足了引用計數(shù)內(nèi)存管理的思考方式。
· 自己生成的對象寓娩,自己所持有叛氨。
· 非自己生成的對象,自己也能持有棘伴。
· 不再需要自己持有的對象時釋放寞埠。
· 非自己持有的對象無法釋放。
__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];//對象A
id test1 = [[Test alloc] init];//對象B
[test0 setObject:test1];
[test1 setObject:test0];
}
當出了作用域之后饭冬,test0和test1分別釋放,但是持有對象A的強引用變?yōu)閷ο驜的_obj,持有對象B的強引用變?yōu)閷ο驛的_obj,從而造成了內(nèi)存泄露(內(nèi)存空間使用完畢之后未回收)揪阶。
像下面這種情況昌抠,雖然只有一個對象,但是該對象對其自身持有鲁僚,會發(fā)生自引用
id test0 = [[Test alloc] init];
[test0 setObject:test0];
那么我們怎樣才能避免這些情況呢炊苫?就是我們所說的__weak修飾符,提供弱引用冰沙,它不會持有對象侨艾,等對象的持有者不存在時,會自動廢棄weak的對象拓挥,我們來看下面的代碼唠梨。
id __weak objc = [[Test alloc] init];
這段代碼編譯器并不會報錯只是會給個警告。這段代碼將自己生成并持有的對象賦值給了帶有__weak修飾符的變量objc撞叽。即變量objc持有對持有對象的弱引用姻成。因此插龄,為了不以自己持有的狀態(tài)來保存自己生成并持有的對象,生成的對象會被立即釋放科展。我們用下面的代碼解決上面的警告均牢。
id __strong objc = [[Test alloc] init];
id __weak weakObj = objc;
像這樣,__weak修飾符可以避免循環(huán)引用的問題才睹,通過檢查__weak修飾的變量是否為空徘跪,可以判斷被賦值的對象是否已經(jīng)廢棄。(在ios4之前琅攘,我們可以用__unsafe_unretained來代替)垮庐。
__unsafe_unretained修飾符
__unsafe_unretained修飾符,是不安全的所有權修飾符坞琴。盡管ARC的內(nèi)存管理是編譯器的工作哨查,但是賦有這個修飾符的變量不屬于編譯器的內(nèi)存管理對象。這一點讀者需要注意剧辐。
id __unsafe_unretained objc = [[Test alloc] init];
賦有該修飾符的變量和__weak修飾符一樣寒亥,因為自己生成并持有的對象不能繼續(xù)為自己所有,所以生成的對象會被立即釋放荧关。但是他們的區(qū)別是什么呢溉奕?想想為什么我們需要使用到它。比如在ios4以前忍啤。賦值給賦有__unsafe_unretained修飾符變量的對象在使用的時候加勤,如果沒有確保其真實存在,那么應用程序就會崩潰同波。而賦有__weak修飾符的變量則會輸出nil鳄梅。
__autoreleasing修飾符
ARC有效的時候我們是不能使用autorelease方法的,另外参萄,也不能使用NSAutoreleasePool類卫枝,這樣一來,雖然autorelease無法使用讹挎,但是ARC下autorelease功能是有效的校赤。如下代碼
@autoreleasepool {
????? id __autoreleasing obj = [[Test alloc] init];
}
我們可以看到“@autoreleasepool”塊代替了原來的NSAutoreleasePool,__autoreleasing代替了原來的 autorelease方法筒溃,即對象被注冊到了自動釋放池马篮。但是顯示的加__autoreleasing修飾符在ARC似乎沒有那么常見。
取得非自己生成并持有對象時怜奖,雖然可以使用alloc/new/copy/mutableCopy以外的方法來取得對象浑测,但該對象已經(jīng)被注冊到自動釋放池了。這同在MRC時取得調(diào)用了autorelease方法的對象是一樣的。這是由于編譯器會檢查方法名是否以alloc/new/copy/mutableCopy開始迁央,如果不是則自動將返回值的對象注冊到自動釋放池里面掷匠。我們看下面的代碼。
@autoreleasepool {
id __strong obj = [NSMutableArray array];
}
我們可以看到不使用__autoreleasing也能將obj注冊到自動釋放池岖圈,這是為什么呢讹语?原因就像我上面講到的,編譯器會自動的查看方法名是否以alloc/new/copy/mutableCopy開始蜂科。我們可以看看array方法里面做了什么顽决。
+ (id)array{
return [[NSMutableArray alloc] init];
}
當然我們也可以這么寫:
+ (id)array{
id __strong obj = [[NSMutableArray alloc] init];
return obj;
}
我們來分析一下,由于return對象的時候出了變量的作用域导匣,所以該強引用的對象會被釋放才菠,但是作為函數(shù)的返回值,編譯器會自動將其注冊到自動釋放池贡定。