Objective-C內存管理之-引用計數


本文會繼續深入學習OC內存管理,內容主要參考iOS高級編程,Objective-C基礎教程,瘋狂iOS講義,是我學習內存管理的筆記

內存管理

1 內存管理的基本概念

1.1 Objective-C中的內存管理

  • 手動內存管理和自動釋放池---MRC>(Mannul Reference Counting)
  • 自動內存管理---ARC>(Automatic Reference Count)
  • 自動垃圾回收---GC>(Garbage Collection)
由於iOS系統不支持垃圾回收,所以我們在iOS開發中只能使用MRC和ARC來進行內存管理,本文不再介紹Objective-C中的垃圾回收機制,但是此處注意Objective-C中是存在垃圾回收機制的

1.2 內存管理中存在的問題

  • 內存泄露:不再需要的對象沒有釋放

    引起的問題:程序的內存占有量不斷增加,最終會被耗盡導致程序崩潰

  • 野指針:沒有進行初始化得指針

    引起的問題:浪費內存資源,如果調用程序會出現未知的結果,甚至導致程序崩潰

  • 懸空指針:一個指針指向一個被銷毀的對象

    引起的問題:調用懸空指針指向的屬性或者方法時,程序會出現未知的結果,甚至導致程序崩潰

  • 僵屍對象:過度釋放的對象

    引起的問題:

2.手動內存管理和自動釋放池---MRC>(Mannul Reference Counting)

2.1 什么是引用計數(Reference Counting)

引用計數:Objective-C中引入了引用計數這一機制來跟蹤並處理對象的生命周期,

管理方式:每個對象都有一個與之關聯的整數,這個整數被稱為引用計數,在Objective-C中,通過不同的方法可以對引用計數進行操作,具體的處理如下表:

對象操作 Objective-C方法 對應的操作結果
生成並持有對象 alloc, new, copy,mutableCopy等方法 生成對象並設置引用計數 =1
持有對象 reatain方法 使引用計數 +1
釋放對象 release方法 使引用計數 -1
廢棄對象 dealloc方法---系統自動調用 引用計數 =0 時調用

關於delloc方法:dealloc方法繼承自NSObject,因此所有的對象都具有此方法,當一個對象的引用計數為0時,也就意味着沒有任何程序需要此對象,系統會回收該對象所占用的內存,在系統銷毀對象之前,會自動調用該對象的dealloc方法來執行一些回收操作,如果該對象還持有其他對象的引用,我們必須重寫dealloc方法來釋放該對象引用的其他對象(通常就是使用該對象的release方法)

引用計數機制回收對象的說明:如果一個對象的引用計數為0,則表明程序已經不再需要它,這時系統會自動回收該對象所占內存,相反,如果一個對象的引用計數不為0,系統就不應該回收,也不會回收它所占的內存

關於retainCount方法:Objective-C提供了retainCount方法來返回一個對象當前的引用計數

如何重寫dealloc方法:

- (void)dealloc {

    // 處理該對象的其他引用(通過release方法)
    
    /** 回調父類的dealloc方法 */
    [super dealloc];
}

2.2 蘋果如何管理引用計數

  • 2.2.1 因為NSObject類的源代碼沒有公開,我們利用Xcode的調試器(lldb)和iOS大概追溯出其實現過程
    • alloc

      +alloc
      +allocWithZone: 
      class_createInstance 		//此方法可以通過objc4中的runtime/objc-runtime-new.mm確認
      calloc						// 分配內存塊
      
    • retainCount

      -retainCount 
      __CFDoExternRefOperation	// 此函數根據retain,retainCount,release操作進行分發,調用__CFBasicHashXXX方法
      CFBasicHashGetCountOfKey
      
    • retain

      -retain
      __CFDoExternRefOperation 
      CFBasicHashAddValue
      
    • release

      -release 
      __CFDoExternRefOperation 
      CFBasicHashRemoveValue		// 當此函數返回0時, -release調用dealloc方法
      
  • 2.2.2 由__CFDoExternRefOperation函數以及此函數的調用關系,我們大概推算蘋果大概是使用散列表(引用計數表)來管理引用計數
    • 通過引用計數表來管理引用計數的好處:
      • 對象用內存塊的分配無須考慮內存塊頭部
      • 引用計數表各記錄中存有內存塊的地址,可從各個記錄追溯到各對象的內存塊(在進行內存泄露的檢查時,此條特性具有舉足輕重的作用,即使出現故障導致對象占用的內存塊損壞,但是只要引用計數表沒有被破壞,我們就可以確定各內存塊的位置,這就是設置全局斷點可以查出哪里出現內存泄露的原因)
        引用計數表

2.3 內存管理的思考方式

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

    1.1 使用alloc new copy mutableCopy創建的對象只能自己持有

    id obj1 = [[NSObject alloc] init];
    id obj2 = [NSObject new];
    id obj3 = [NSObject copy];
    id obj4 = [NSObject mutableCopy];
    

    1.2 使用以上名稱的開頭的方法也意味着自己生成並持有對象

    allocNewObject

    newNewObject

    copyNewObject

    mutableCopyNewObject

  2. 非自己生成的對象,自己也能持有

    2.1 非alloc new copy mutableCopy生成的對象,變量obj本身不持有該對象

    id obj1 = [NSMutableArray array];
    id obj2 = [NSDictionary dictionary];
    
    

    2.2 通過retain方法,非通過alloc new copy mutableCopy生成的對象,可以成為自己持有的對象

    id obj = [NSMutableArray array];
    
    [obj retain];
    
  3. 不再需要自己持有的對象時釋放

    • 3.1 釋放通過alloc new copy mutableCopy生成的對象,一旦不在需要,務必要使用release方法釋放

      id obj = [[NSObject alloc] init];
       
      [obj release];
      
    • 3.2 用retain方法持有的非自己生成的對象,一旦不再需要,也一定要使用release釋放

      id obj = [NSMutableArray array];
      [obj retain]; 	// 通過retain方法持有對象
      [obj release];	// 在不需要時也要通過release方法釋放對象
      
    • 3.3 用某個方法生成對象,並將其作為方法的返回值,這時我們該如何處理

      • 3.3.1 通過alloc new copy mutableCopy 或其他符合命名規則的方法生成的對象,只需要原封不動的返回就能讓調用方也持有該對象

        - (id)allocObject {
        	id obj = [[NSObject alloc] init];
            return obj;
        }
        
        - (id)allocObjectWithObject:(id)obj {
        	id object = [obj allocObject];
        	return object;
        }	    
        
      • 3.3.2 如果持有非自己生成的對象,例如[NSMutableArray array]生成的對象,我們要使用autorelease方法釋放

        注:命名規則:用來取得誰都不持有的對象的方法名不能以alloc new copy mutableCopy開頭

        - (id)object {  
        	 id obj = [NSMutableArray array];
        	 [obj autorelease];
        	 return obj;
        }
        
      • 3.3.3 autorelease方法:提供了這樣的功能,使對象在超出指定的生存范圍時自動並正確釋放(調用release方法)
        release和autorelease的區別

  4. 非自己持有的對象無法釋放---注意以下兩點,如果發生這樣的情況會導致程序崩潰

    • 4.1 通過alloc new copy mutableCopy方法或者通過retain方法持有的對象,一旦不再需要時,必須進行釋放,除此之外其他方法獲得的對象絕對不能釋放,一旦釋放會造成程序崩潰

    • 4.2 自己持有的對象釋放后再次釋放,造成僵死對象,引起程序崩潰或在訪問廢棄的對象時崩潰

      id obj = [[NSObject alloc] init];
      [obj release]; 
      [obj release]; 	// 再次釋放
      


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM