ARC[轉(zhuǎn)]

對象操作 Objective-C方法
生成并持有對象 alloc/new/copy/mutableCopy等方法
持有對象 retain方法
釋放對象 release方法
廢棄對象 dealloc方法

自己生成的對象肉康,自己持有
非自己生成的對象境蔼,自己也能持有
不再需要自己持有對象時釋放
無法釋放非自己持有的對象

autorelease

顧名思義:autorelease就是自動釋放魔慷,它和C語言的局部變量類似咬清,C語言局部變量在程序執(zhí)行時捐凭,超出其作用域時记舆,會被自動廢棄鸽捻。autorelease會像C語言局部變量那樣對待對象實例,當超出作用域時,對象實例的release實例方法會被調(diào)用御蒲。我們可以設定autorelease的作用域衣赶。

autorelease具體使用方法

  • 生成并持有NSAutoreleasePool對象
  • 調(diào)用已分配對象的autorelease實例方法
  • 廢棄NSAutoreleasePool對象
1.png

NSAutoreleasePool生命周期

NSAutoreleasePool對象的生命周期就是一個作用域,對于所有調(diào)用過autorelease實例方法的對象厚满,在廢棄NSAutoreleasePool對象時府瞄,都會調(diào)用其release實例方法。如上圖所示碘箍。

用源代碼表示如下:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];

在[pool drain]調(diào)用時遵馆,NSAutoreleasePool被銷毀,obj的release方法會被觸發(fā)丰榴。obj被釋放货邓。

所有權修飾符及其原理

在 ARC 特性下有 4 種與內(nèi)存管理息息相關的變量所有權修飾符值得我們關注:

  • __strong
  • __weak
  • __unsafe_unretaied
  • __autoreleasing

說到變量所有權修飾符,有人可能會跟屬性修飾符搞混四濒,這里做一個對照關系小結:

  • assign 對應的所有權類型是 __unsafe_unretained换况。
  • copy 對應的所有權類型是 __strong。
  • retain 對應的所有權類型是 __strong盗蟆。
  • strong 對應的所有權類型是 __strong戈二。
  • unsafe_unretained對應的所有權類型是__unsafe_unretained。
  • weak 對應的所有權類型是 __weak喳资。

__strong

原理:

    {
        id __strong obj = [[NSObject alloc] init];
    }
    //編譯器的模擬代碼
    id obj = objc_msgSend(NSObject,@selector(alloc));
    objc_msgSend(obj,@selector(init));
    // 出作用域的時候調(diào)用
    objc_release(obj);

對象是通過除alloc觉吭、new、copy仆邓、multyCopy外方法產(chǎn)生的情況
對于非自己持有的對象

{
id __strong obj = [NSMutableArray array];
}

結果與之前稍有不同:

//編譯器的模擬代碼
id obj = objc_msgSend(NSMutableArray,@selector(array));
objc_retainAutoreleasedReturnValue(obj);
objc_release(obj);

objc_retainAutoreleasedReturnValue函數(shù)主要用于優(yōu)化程序的運行亏栈。它是用于持有(retain)對象的函數(shù),它持有的對象應為返回注冊在autoreleasePool中對象的方法宏赘,或是函數(shù)的返回值绒北。像該源碼這樣,在調(diào)用array類方法之后察署,由編譯器插入該函數(shù)闷游。

而這種objc_retainAutoreleasedReturnValue函數(shù)是成對存在的,與之對應的函數(shù)是objc_autoreleaseReturnValue贴汪。它用于array類方法返回對象的實現(xiàn)上脐往。下面看看NSMutableArray類的array方法通過編譯器進行了怎樣的轉(zhuǎn)換:

+ (id)array 
{
return [[NSMutableArray alloc] init];
}

//編譯器模擬代碼
+ (id)array 
{
id obj = objc_msgSend(NSMutableArray,@selector(alloc));
objc_msgSend(obj,@selector(init));
// 代替我們調(diào)用了autorelease方法
return objc_autoreleaseReturnValue(obj);
}

我們可以看見調(diào)用了objc_autoreleaseReturnValue函數(shù)且這個函數(shù)會返回注冊到自動釋放池的對象,但是扳埂,這個函數(shù)有個特點业簿,它會查看調(diào)用方的命令執(zhí)行列表,如果發(fā)現(xiàn)接下來會調(diào)用objc_retainAutoreleasedReturnValue則不會將返回的對象注冊到autoreleasePool中而僅僅返回一個對象阳懂。達到了一種最優(yōu)效果梅尤。如下圖:

1.png

__weak

__weak 表示弱引用柜思,對應定義 property 時用到的 weak。弱引用不會影響對象的釋放巷燥,而當對象被釋放時赡盘,所有指向它的弱引用都會自定被置為 nil,這樣可以防止野指針缰揪。使用__weak修飾的變量陨享,即是使用注冊到autoreleasePool中的對象。__weak 最常見的一個作用就是用來避免循環(huán)循環(huán)钝腺。需要注意的是抛姑,__weak 修飾符只能用于 iOS5 以上的版本,在 iOS4 及更低的版本中使用 __unsafe_unretained 修飾符來代替艳狐。

__weak 的幾個使用場景:
在 Delegate 關系中防止循環(huán)引用途戒。
在 Block 中防止循環(huán)引用。
用來修飾指向由 Interface Builder 創(chuàng)建的控件僵驰。比如:@property (weak, nonatomic) IBOutlet UIButton *testButton;。

原理

    {
        id __weak obj = [[NSObject alloc] init];
    }
編譯器轉(zhuǎn)換后的代碼如下:
    id obj;
    id tmp = objc_msgSend(NSObject,@selector(alloc));
    objc_msgSend(tmp,@selector(init));
    objc_initweak(&obj,tmp);
    objc_release(tmp);
    objc_destroyWeak(&object);

對于__weak內(nèi)存管理也借助了類似于引用計數(shù)表的散列表唁毒,它通過對象的內(nèi)存地址做為key蒜茴,而對應的__weak修飾符變量的地址作為value注冊到weak表中,在上述代碼中objc_initweak就是完成這部分操作浆西,而objc_destroyWeak

則是銷毀該對象對應的value粉私。當指向的對象被銷毀時,會通過其內(nèi)存地址近零,去weak表中查找對應的__weak修飾符變量诺核,將其從weak表中刪除。所以久信,weak在修飾只是讓weak表增加了記錄沒有引起引用計數(shù)表的變化.

對象通過objc_release釋放對象內(nèi)存的動作如下:

  • objc_release
  • 因為引用計數(shù)為0所以執(zhí)行dealloc
  • _objc_rootDealloc
  • objc_dispose
  • objc_destructInstance
  • objc_clear_deallocating

而在對象被廢棄時最后調(diào)用了objc_clear_deallocating窖杀,該函數(shù)的動作如下:
從weak表中獲取已廢棄對象內(nèi)存地址對應的所有記錄
將已廢棄對象內(nèi)存地址對應的記錄中所有以weak修飾的變量都置為nil
從weak表刪除已廢棄對象內(nèi)存地址對應的記錄
根據(jù)已廢棄對象內(nèi)存地址從引用計數(shù)表中找到對應記錄刪除
據(jù)此可以解釋為什么對象被銷毀時對應的weak指針變量全部都置為nil,同時裙士,也看出來銷毀weak步驟較多入客,如果大量使用weak的話會增加CPU的負荷。

還需要確認一點是:__weak修飾符的變量腿椎,即是使用注冊到autoreleasePool中的對象桌硫。

    {
        id __weak obj1 = obj; 
        NSLog(@"obj2-%@",obj1);
    }
編譯器轉(zhuǎn)換上述代碼如下:
    id obj1; 
    objc_initweak(&obj1,obj);
    id tmp = objc_loadWeakRetained(&obj1);
    objc_autorelease(tmp);
    NSLog(@"%@",tmp);
    objc_destroyWeak(&obj1);

objc_loadWeakRetained函數(shù)獲取附有__weak修飾符變量所引用的對象并retain, objc_autorelease函數(shù)將對象放入autoreleasePool中,據(jù)此當我們訪問weak修飾指針指向的對象時啃炸,實際上是訪問注冊到自動釋放池的對象铆隘。因此,如果大量使用weak的話南用,在我們?nèi)ピL問weak修飾的對象時膀钠,會有大量對象注冊到自動釋放池,這會影響程序的性能掏湾。

解決方案:要訪問weak修飾的變量時,先將其賦給一個strong變量托修,然后進行訪問

為什么訪問weak修飾的對象就會訪問注冊到自動釋放池的對象呢?

因為weak不會引起對象的引用計數(shù)器變化忘巧,因此,該對象在運行過程中很有可能會被釋放睦刃。所以砚嘴,需要將對象注冊到自動釋放池中并在autoreleasePool銷毀時釋放對象占用的內(nèi)存。

__unsafe_unretained

ARC 是在 iOS5 引入的涩拙,而 __unsafe_unretained 這個修飾符主要是為了在ARC剛發(fā)布時兼容iOS4以及版本更低的系統(tǒng)际长,因為這些版本沒有弱引用機制。這個修飾符在定義property時對應的是unsafe_unretained兴泥。__unsafe_unretained 修飾的指針純粹只是指向?qū)ο蠊び瑳]有任何額外的操作,不會去持有對象使得對象的 retainCount +1搓彻。而在指向的對象被釋放時依然原原本本地指向原來的對象地址如绸,不會被自動置為 nil,所以成為了野指針旭贬,非常不安全怔接。

__unsafe_unretained 的應用場景:

在 ARC 環(huán)境下但是要兼容 iOS4.x 的版本,用__unsafe_unretained 替代 __weak 解決強循環(huán)循環(huán)的問題稀轨。

__autoreleasing

將對象賦值給附有__autoreleasing修飾符的變量等同于MRC時調(diào)用對象的autorelease方法扼脐。

    @autoeleasepool {
        // 如果看了上面__strong的原理,就知道實際上對象已經(jīng)注冊到自動釋放池里面了 
        id __autoreleasing obj = [[NSObject alloc] init];
    }
編譯器轉(zhuǎn)換上述代碼如下:
    id pool = objc_autoreleasePoolPush(); 
    id obj = objc_msgSend(NSObject,@selector(alloc));
    objc_msgSend(obj,@selector(init));
    objc_autorelease(obj);
    objc_autoreleasePoolPop(pool);
@autoreleasepool {
        id __autoreleasing obj = [NSMutableArray array];
    }
編譯器轉(zhuǎn)換上述代碼如下:

    id pool = objc_autoreleasePoolPush();
    id obj = objc_msgSend(NSMutableArray,@selector(array));
    objc_retainAutoreleasedReturnValue(obj);
    objc_autorelease(obk);
    objc_autoreleasePoolPop(pool);

上面兩種方式奋刽,雖然第二種持有對象的方法從alloc方法變?yōu)榱薿bjc_retainAutoreleasedReturnValue函數(shù)瓦侮,都是通過objc_autorelease,注冊到autoreleasePool中佣谐。

ARC 模式規(guī)則

ARC 模式下肚吏,還有一些需要注意的規(guī)則:

  • 不能顯式使用 retain/release/retainCount/autorelease。
  • 不能使用 NSAllocateObject/NSDeallocateObject狭魂。
  • 需要遵守內(nèi)存管理的方法命名規(guī)則须喂。在 ARC 模式和 MRC 模式下,以 alloc/new/copy/mutableCopy 開頭的方法在返回對象時都必須返回給調(diào)用方所應當持有的對象趁蕊。在 ARC 模式下坞生,追加一條:以 init 開頭的方法必須是實例方法并且必須要返回對象。返回的對象應為 id 類型或聲明該方法的類的對象類型掷伙,或是該類的超類型或子類型是己。該返回的對象并不注冊到 Autorelease Pool 中,基本上只是對 alloc 方法返回值的對象進行初始化處理并返回該對象任柜。需要注意的是:- (void)initialize; 方法雖然是以 init 開頭但是并不包含在上述規(guī)則中卒废。
  • 不要顯式調(diào)用 dealloc沛厨。
  • 使用 @autoreleasepool 塊替代 NSAutoreleasePool。
  • 不能使用區(qū)域(NSZone)摔认。
  • 對象型變量不能作為 C 語言結構體(struct/union)的成員逆皮。
  • 顯式轉(zhuǎn)換 id 和 void *。

Toll-Free Bridging [MRC ARC 轉(zhuǎn)化]

MRC 下的 Toll-Free Bridging 因為不涉及內(nèi)存管理的轉(zhuǎn)移参袱,相互之間可以直接交換使用电谣,當使用 ARC 時,由于Core Foundation 框架并不支持 ARC抹蚀,此時編譯器不知道該如何處理這個同時有 ObjC 指針和 CFTypeRef 指向的對象剿牺,所以除了轉(zhuǎn)換類型,還需指定內(nèi)存管理所有權的改變环壤,可通過 __bridge晒来、__bridge_retained 和 CFBridgingRetain、__bridge_transfer 和 CFBridgingRelease郑现。

__bridge

只是聲明類型轉(zhuǎn)變湃崩,但是不做內(nèi)存管理規(guī)則的轉(zhuǎn)變。比如:

CFStringRef s1 = (__bridge CFStringRef) [[NSString alloc] initWithFormat:@"Hello, %@!", name];

只是做了 NSString 到 CFStringRef 的轉(zhuǎn)化接箫,但管理規(guī)則未變攒读,依然要用 Objective-C 類型的 ARC 來管理 s1,你不能用 CFRelease() 去釋放 s1列牺。

__bridge_retained or CFBridgingRetain

表示將指針類型轉(zhuǎn)變的同時,將內(nèi)存管理的責任由原來的 Objective-C 交給Core Foundation 來處理拗窃,也就是瞎领,將 ARC 轉(zhuǎn)變?yōu)?MRC。比如随夸,還是上面那個例子

NSString *s1 = [[NSString alloc] initWithFormat:@"Hello, %@!", name];
CFStringRef s2 = (__bridge_retained CFStringRef)s1;
// or CFStringRef s2 = (CFStringRef)CFBridgingRetain(s1);
// do something with s2
//...
CFRelease(s2); // 注意要在使用結束后加這個

我們在第二行做了轉(zhuǎn)化九默,這時內(nèi)存管理規(guī)則由 ARC 變?yōu)榱?MRC,我們需要手動的來管理 s2 的內(nèi)存宾毒,而對于 s1驼修,我們即使將其置為 nil,也不能釋放內(nèi)存诈铛。

__bridge_transfer or CFBridgingRelease

這個修飾符和函數(shù)的功能和上面那個 __bridge_retained 相反乙各,它表示將管理的責任由 Core Foundation 轉(zhuǎn)交給 Objective-C,即將管理方式由 MRC 轉(zhuǎn)變?yōu)?ARC幢竹。比如:

CFStringRef result = CFURLCreateStringByAddingPercentEscapes(. . .);
NSString *s = (__bridge_transfer NSString *)result;
//or NSString *s = (NSString *)CFBridgingRelease(result);
return s;

這里我們將 result 的管理責任交給了 ARC 來處理耳峦,我們就不需要再顯式地將 CFRelease() 了。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末焕毫,一起剝皮案震驚了整個濱河市蹲坷,隨后出現(xiàn)的幾起案子驶乾,更是在濱河造成了極大的恐慌,老刑警劉巖循签,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件级乐,死亡現(xiàn)場離奇詭異,居然都是意外死亡县匠,警方通過查閱死者的電腦和手機风科,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來聚唐,“玉大人丐重,你說我怎么就攤上這事「瞬椋” “怎么了扮惦?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長亲桦。 經(jīng)常有香客問我崖蜜,道長,這世上最難降的妖魔是什么客峭? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任豫领,我火速辦了婚禮,結果婚禮上舔琅,老公的妹妹穿的比我還像新娘等恐。我一直安慰自己,他們只是感情好备蚓,可當我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布课蔬。 她就那樣靜靜地躺著,像睡著了一般郊尝。 火紅的嫁衣襯著肌膚如雪二跋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天流昏,我揣著相機與錄音扎即,去河邊找鬼。 笑死况凉,一個胖子當著我的面吹牛谚鄙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播刁绒,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼襟锐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了膛锭?” 一聲冷哼從身側(cè)響起粮坞,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤蚊荣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后莫杈,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體互例,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年筝闹,在試婚紗的時候發(fā)現(xiàn)自己被綠了媳叨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡关顷,死狀恐怖糊秆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情议双,我是刑警寧澤痘番,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站平痰,受9級特大地震影響汞舱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宗雇,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一昂芜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赔蒲,春花似錦泌神、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至砾嫉,卻和暖如春幼苛,著一層夾襖步出監(jiān)牢的瞬間窒篱,已是汗流浹背焕刮。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留墙杯,地道東北人配并。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像高镐,于是被迫代替她去往敵國和親溉旋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容