天天看點

記憶體管理機制

1.什麼是 ARC?

ARC 是 iOS 5 引入的記憶體管理新功能 – 自動引用計數 。它的工作原理大緻是這樣:當我們編譯源碼時,編譯器會分析源碼中每個對象的生命周期,然後基于這些對象的生命周期,來添加相應的引用計數操作代碼。是以,ARC 是工作在編譯期的一種技術方案。

這樣的好處是:編譯之後,ARC 與非 MRC 代碼是沒有什麼差别的,是以二者可以在源碼中共存。實際上,你可以通過編譯參數 -fno-objc-arc 來關閉部分源代碼的 ARC 特性。由于 ARC 能夠深度分析每一個對象的生命周期,它能夠做到比 MRC 更加高效。

例如在一個函數中,對一個對象剛開始有一個引用計數 +1 的操作,之後又緊接着有一個 -1 的操作,那麼編譯器就可以把這兩個操作都優化掉。

2 ARC 的核心思想?

自己生成的對象,自己持有

非自己生成的對象,自己可以持有

自己持有的對象不再需要時,需要對其進行釋放

非自己持有的對象無法釋放

3 ARC 在使用時應該遵循的原則?

不能使用 retain、release、retainCount、autorelease。

不可以使用 NSAllocateObject、NSDeallocateObject。

必須遵守記憶體管理方法的命名規則。

不需要顯示的調用 Dealloc。

使用 @autoreleasePool 來代替 NSAutoreleasePool。

不可以使用區域 NSZone。

對象性變量不可以作為 C 語言的結構體成員。

顯示轉換 id 和 void*。

4 ARC 在編譯時做了哪些工作?

自動調用 保留(retain) 與 釋放(release) 的方法

相對于垃圾回收這類記憶體管理方案,ARC 不會帶來運作時的額外開銷,是以對于應用的運作效率不會有影響。 ARC 會把能夠互相抵消 retain、release、autorelease,操作簡化,如果發現在同一個對象上執行了多次保留與釋放操作,那麼 ARC 有時可以成對的移除這兩個操作。

5 ARC 在運作時做了哪些工作?

主要是指 weak 關鍵字。weak 修飾的變量能夠在引用計數為0 時被自動設定成 nil,顯然是有運作時邏輯在工作的。關于原因會單獨開一個問題

為了保證向後相容性,ARC 在運作時檢測到類函數中的 autorelease 後緊跟其後 retain,此時不直接調用對象的 autorelease 方法,而是改為調用 objc_autoreleaseReturnValue。

objc_autoreleaseReturnValue 會檢視目前方法傳回之後即将要執行的那段代碼,若那段代碼要在傳回對象上執行 retain 操作,則設定全局資料結構中的一個标志位,而不執行 autorelease 操作,與之相似,如果方法傳回了一個自動釋放的對象,而調用方法的代碼要保留此對象,那麼此時不直接執行 retain ,而是改為執行 objc_retainAoutoreleasedReturnValue函數。此函數要檢測剛才提到的标志位,若已經置位,則不執行 retain 操作,設定并檢測标志位,要比調用 autorelease 和retain更快。

_myPerson = [ECOPerson personWithName:@“Bob”]; 
ECOPerson * tmp = [ECOPerson personWithName:@“Bob”]; 
_myPerson = [tmp retain];
           

6 函數傳回一個對象時,會對對象autorelease麼?為什麼?autorelease是什麼時候釋放的?

會對對象 autorelease,因為需要在稍後釋放對象,進而給調用者留下足夠長的時間,使其可以在需要時先保留傳回值。此方法可以保證對象在跨越方法調用邊界時一定存活。

除非你有自己的自動釋放池,否則這個時機就是目前線程,目前事件循環結束時,就是 RunLoop 結束時(observer -> beforeWaiting)。

// 情況一:
@autoreleasepool {
    NSObject * obj = [NSObject new];
    [obj autorelease];
    NSLog(@"%d",[obj retainCount]); //1
}
           
// 情況二:
NSObject * obj = [NSObject new];
[obj autorelease];
[obj autorelease];
NSLog(@"%d",[obj retainCount]); 
NSLog(@"%d",[obj retainCount]);   

// 崩潰
           
// 情況三:
NSObject * obj;
@autoreleasepool {
        obj = [NSObject new];
        [obj autorelease];
        NSLog(@"%d",[obj retainCount]);  // 1
  }
//crash  出了大括号就已經被銷毀了
NSLog(@"%d",[obj retainCount]);
           

7 為什麼已經有了 ARC ,還需要 @autoreleasePool?

提到 OC 的 RC,首先要橫向對比一下 Android 的 GC(垃圾回收機制),GC 的記憶體回收是集中式回收(定期回收),而 RC 的回收是伴随整個運作時的,是以 android 機器有種 時“卡”時“流暢” 的感覺,而 iOS 總體比較均勻,缺乏像 GC 的集中式回收記憶體的類似機制,是以猜測 Pool的産生也是彌補 RC 的這一不足,在 RC 基礎上進行記憶體優化的一種手段。

8 簡要闡述記憶體相關的關鍵字?

Strong

Strong 修飾符表示指向并持有該對象,其修飾對象的引用計數會加1。該對象隻要引用計數不為0就不會被銷毀。當然可以通過将變量強制指派 nil 來進行銷毀。

Weak

weak 修飾符指向但是并不持有該對象,引用計數也不會加1。在 Runtime 中對該屬性進行了相關操作,無需處理,可以自動銷毀。weak用來修飾對象,多用于避免循環引用的地方。weak 不可以修飾基本資料類型。

assign

assign主要用于修飾基本資料類型,

例如NSInteger,CGFloat,存儲在棧中,記憶體不用程式員管理。assign是可以修飾對象的,但是會出現問題。

copy

copy關鍵字和 strong類似,copy 多用于修飾有可變類型的不可變對象上 NSString,NSArray,NSDictionary上。

__unsafe_unretain

__unsafe_unretain 類似于 weak ,但是當對象被釋放後,指針已然儲存着之前的位址,被釋放後的位址變為 僵屍對象,通路被釋放的位址就會出問題,是以說他是不安全的。

__autoreleasing

繼續閱讀