Objective-C的內存管理(2)——從MRC到ARC

羅裏吧嗦顛三倒四,單純的我的筆記。html

MRC

引用計數上一篇已經有大概講過。在Objective-C裏,每一個繼承自NSObject的對象都會記錄自身的引用計數,一番加加減減以後,變成0就會釋放掉。
MRC是Mannul Reference Counting的縮寫,意思也很簡單,這番加加減減都靠手動管理的意思。segmentfault

使用時的基本原則是:管好本身。每一個對象,引用別的對象時加了幾回計數最終到了不用的時候就要減幾回。不能多也不能少。
這樣就聚焦了不少。框架

致使引用計數增長的操做,顯式的retain很少說,剩下的就是四個關鍵字:alloc、new(以及new開頭的方法)、copy、mutableCopy,使用這四個關鍵字獲得的對象,就算你本身加的引用計數,回頭要本身減掉。函數

引用計數減小的操做就是release了。oop

AutoRelease

AutoReleasePool是個自動釋放池,加入其中的對象會延遲到Pool「結束」時釋放。
在MRC中,你能夠顯式建立一個NSAutoReleasePool,顯式地將一個對象加入進去,並顯式釋放AutoReleasePool:this

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Code benefitting from a local autorelease pool.
NSObject *obj = [[[NSObject alloc] init] autorelease];
[pool release];//[pool drain];

在ARC中,須要經過特殊的語法:code

@autoreleasepool {
    // Code benefitting from a local autorelease pool.
}

默認地,每一個Runloop迭代開始時會建立一個AutoReleasePool,Runloop迭代結束時釋放。也就是說,當咱們沒有顯式建立AutoReleasePool時,autorelease的對象會在Runloop迭代結束時釋放。
當咱們顯式地建立AutoReleasePool時,其釋放時機是咱們決定的(顯式調用release/drain或block結束)。orm

主動使用AutoReleasePool的目的一般是爲了控制內存峯值。好比,我有一個大循環,每次循環都會建立比較大的autorelease的臨時對象。若是不顯式釋放,這些臨時對象會在整個循環結束後才一塊兒釋放,期間可能形成內存佔用太高。這種狀況下就能夠在每次循環內聲明autoreleasepool,保證臨時對象不會堆積。htm

ARC

ARC爲咱們自動地作了不少,屏蔽了不少細節,理論上來講,咱們只須要關注對象間的全部權關係便可。
上層機制雖然簡單,涉及到的細節仍是有不少的,可喜可賀的是,ARC是有標準文檔的。簡直...對象

ARC提供的變量修飾符有如下幾個:

  • __strong
  • __weak
  • __unsafe_unretaied
  • __autoreleasing

提供的屬性修飾符有:

  • assign 對應的全部權類型是 __unsafe_unretained。
  • copy 對應的全部權類型是 __strong。
  • retain 對應的全部權類型是 __strong。
  • strong 對應的全部權類型是 __strong。
  • unsafe_unretained對應的全部權類型是__unsafe_unretained。
  • weak 對應的全部權類型是 __weak。

(基本類型默認是assign,對象類型默認是strong)

__strong

強引用,很少說了。注意聲明變量和屬性時若未加說明,默認是強引用。

__weak

弱引用。當對象被釋放時,weak修飾的變量會被置爲nil。
仔細想一想,想要實現這個特性,全部的weak變量都須要放到一個全局的map裏,實現成本仍是比較高的。

__unsafe_unretained

不作任何額外操做。

__autoreleasing

__autoreleasing標記的變量等價於調用autorelease方法

想到一個小問題:對於函數返回值,ARC是怎麼知道要不要加引用計數呢?
看這幾行代碼:

- (void)testMethod
{
    NSObject *obj = [NSObject new];
    NSArray *array = [NSArray array];
    // do something
}

在ARC中,obj和array用完以後都會被自動釋放,可是細想之下其實有很多細節。
要知道,[NSObject new]返回的對象引用計數是有+1的,而[NSArray array]並非。
這倆玩意兒引用計數差了1,ARC是怎麼知道誰要多釋放一次的?
在MRC中,咱們知道new出來的obj須要手動釋放,而array就不須要,是經過方法的關鍵詞進行判斷。
可是方法中的關鍵詞不該該是某種約定嗎?ARC難道也會去看一個方法是不是以new開頭?
看了文檔以後發現...ARC還真是這麼作的...

Methods in the alloc, copy, init, mutableCopy, and new families are implicitly marked __attribute__((ns_returns_retained)).

回想起來,在MRC時代,這些關鍵詞應該是止步於約定的。而ARC或許是爲了平滑過渡,把曾經的約定變成了語法規範,emmm,感受這麼搞不是很好啊。

Block內存管理

在ARC以後,內存管理的問題減小了不少,但仍然有一些遺留。其中最重要的一部分就是Block相關的內存管理。
參考Objective-C中Block的循環引用問題

Bridge

Core Foundation框架 (CoreFoundation.framework) 是一組C語言接口,它們爲iOS應用程序提供基本數據管理和服務功能。
Objective-C對象和CF對象是能夠直接轉換的:

CFStringRef aCFString = (CFStringRef)aNSString;
NSString *aNSString = (NSString *)aCFString;

然而ARC是不支持CF對象的內存管理的,這就須要咱們關注誰來釋放轉換後的對象的問題。

在MRC中,相對來講比較簡單,CFRelease和release方法是等效的,擇機使用便可。
這裏主要關注ARC下的狀況。
根據不一樣需求,有3種轉換方式

  • __bridge                  (不改變對象全部權)
  • __bridge_retained 或者 CFBridgingRetain()               (解除 ARC 全部權)
  • __bridge_transfer 或者 CFBridgingRelease()             (給予 ARC 全部權)

1. __bridge

__bridge不改變對象全部權。

  1. OC對象轉CF,仍由ARC管理,不須要顯式釋放
NSString *aNSString = [[NSString alloc]initWithFormat:@"test"];
CFStringRef aCFString = (__bridge CFStringRef)aNSString;
// do something
  1. CF對象轉OC,仍由CF管理,須要顯式釋放
CFStringRef aCFString = CFStringCreateWithCString(NULL, "test", kCFStringEncodingASCII);
NSString *aNSString = (__bridge NSString *)aCFString;
// do something
CFRelease(aCFString);

2. __bridge_retained

全部權給CF,所以要調用CFRelease顯式釋放

NSString *aNSString = [[NSString alloc]initWithFormat:@"test"];
CFStringRef aCFString = (__bridge_retained CFStringRef) aNSString;
// do something
CFRelease(aCFString);

3. __bridge_transfer

全部權給ARC,所以無需手動管理

CFStringRef aCFString = CFStringCreateWithCString(NULL, "test", kCFStringEncodingASCII);
NSString *aNSString = (__bridge_transfer NSString *)aCFString;
// do something
相關文章
相關標籤/搜索