Objective-c 內存管理的歷史和參考資料

jopen 9年前發布 | 20K 次閱讀 Objective-C開發 Objective-C

對于像我這樣自學IOS開發的初學者,網上有很多資料,很多教程,但是過于多的資料也使初學者無法篩選合適自己的。并且Objective- c也經歷了幾個階段的演變,初學者有時更加會覺得迷茫了。本文總結一下Objective-c在內存管理上的一些問題,整理一些合適的資料。

早期:引用計數的內存管理

Objective-c繼承自C,擁有一套基于對象引用計數的內存管理體系。這跟C#或者Java的垃圾回收機制不同,倒是與C++后來的智能指針有些類似。這種引用計數的思想在很多地方也有應用,不過本質上說,仍然是需要程序員來手動管理計數的,只不過當計數值清零時,對象會被自動釋放罷了。

類似的代碼如下,請注意注釋:

//申請內存,并調用初始化,返回指針
//alloc會使得對象的引用計數初始化為1
Engine *engine1 = [[Engine alloc] init]; 

//調用retain,等同于使引用計數+1
[engine1 retain];

//調用release,等同于使引用計數-1
//只有當引用計數為0時,對象才會釋放
[engine1 release];

即程序員需要來回的使用alloc、retain、release控制對象的引用計數

屬性與編譯器約定

在遇到復雜面向對象編程的時候更為棘手,假設有一個類,其中包含有多個屬性,這些屬性都是指針類型,指向堆上的內存:

@interface Person
{
    NSString *Name;
    NSString *Address;
}

于是乎,當無論在什么時候,Person類的實例都將擁有對Name和Address兩塊內存的所有權。如果申請這兩塊內存的代碼不是Person類自己的代碼。那么Person需要使用retain增加引用計數,并在析構中負責release。那么很容易想到使用set和get這對方法,對類的字段進行封裝,以Name為例:

-(void) setName:(NSString *)name{
    if(self.Name != name){
        [Name release];
        Name = [name retain];
    }
}

-(NSString *) getName {
    return Name;
}

-(void)dealloc{
    [self setName:nil];
    [super dealloc];
}

在上面的set和get方法中,封裝了對內存的管理部分。為了簡化這種需求,編譯器直接通過編譯支持這種寫法,即我們可以通過下面的代碼代替上面的set和get函數,編譯器會自動按照上面的代碼展開:

@interface Person
@property (retain) NSString* Name;
@end


@implementation Person
@synthesize Name;
@end

于是在使用屬性的時候可以這么做:

Person *person = [[Person alloc] init];
person.Name = @"Jack";

實際上編譯器對上述調用代碼同樣會展開成setName函數。這就是“點語法”,看起來是不是跟大部分的語言的屬性訪問有些接近了。

自動釋放池

后來開始出現自動釋放池(Auto release pool),這個東西的出現簡單的說是基于一種十分常見確很難處理的場景:需要在一個方法內部申請內存,并將內存的地址返回給調用者,這個時候誰來釋放這個內存?如下面的代碼:

-(ClassA *) Func1
{
    //內部申請空間,obj引用計數為1
    ClassA *obj = [[ClassA alloc] init];
    //返回這個申請的內存
    return obj;
    //誰來負責釋放???
}

這個梗其實在任何非垃圾回收的語言中都存在,需要人為約定,要么創建者釋放,要么調用者釋放。試想,如果是創建者釋放,那么創建者怎么知道調用者什么時候結束調用呢?如果是調用者釋放,又會因為協同開發的原因,造成調用者忘記釋放的情況。

OC發明了自動釋放池的概念,上面的代碼可以這樣寫:

-(ClassA *) Func1
{
    //內部申請空間,obj引用計數為1,使用autorelease延遲釋放
    ClassA *obj = [[[ClassA alloc] init] autorelease];
    //返回這個申請的內存
    return obj;
}

使用autorelease方法,實際上的結果是本地變量obj的引用在自動釋放池中保留一份,并在恰當的時候,釋放掉。具體原理參加下面給出的資料鏈接。

事實上從程序結構上看,OC選擇了誰申請誰釋放的原則。至此,著名的黃金法則出現了,遵循黃金法則的代碼不會出現內存泄露

The basic rule to apple is everything that increases the reference counter with alloc,[mutable]copy[WithZone:] or retain is in charge of the corresponding [auto]release. 如果一個對象使用了alloc,[mutable] copy,retain,那么必須使用相應的release或autorelease

詳細的早期內存管理的資料參考:

iOS:內存管理(一):OC中的內存管理

iOS:內存管理(二):怎樣在xcode里面使用Memory Leaks和Instruments教程

iOS:內存管理(三):在Objective-c里面使用property教程

Objective-C的內存管理(一)黃金法則的理解

Objective-C的內存管理(二)autorelease的理解

上面是早期的內存管理原理的機制,個人覺得還是需要深刻理解的,雖然現在基本都不這么寫了,下面會說到

目前:自動引用計數

自動引用計數(Auto reference counting)后來被引入,實際上是編譯器功能的大幅增強。現在開發IOS,默認都是開啟ARC功能的。并在在ARC下,甚至是不允許寫release、retain、autorelease這樣的方法的。如果初學者從網上的資料中學習了,上面早期的一些知識,需要注意,實際做的時候基本都是使用ARC的(不少較新的OC程序員甚至已經不會手動管理內存了)。個人實際操作下來,感覺使用ARC,基本跟寫C#沒有什么太大的差別。這真心是有賴于編譯器的強大。

在ARC下,并沒有少了引用計數這個環節,從ARC的字面來看,實際上是編譯器幫程序員完成了對引用計數的增減。而且ARC也有相應的編碼約定或規范,在學習之前,還是需要學習早期的內存管理體系的。

參考資料如下:

轉向ARC的說明——翻譯Apple官方文檔

本文總結了關于Objective-c中的內存管理方面的基礎內容,希望對初學者有個啟發。

來自:http://www.pchou.info/ios/2015/06/05/oc-memory-management.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!