Objective-C的記憶體管理(一)黃金法則的理解

來源:互聯網
上載者:User

一、記憶體管理黃金法則:

The basic rule to apple is everything thatincreases the reference counter with alloc,[mutable]copy[WithZone:] or retainis in charge of the corresponding [auto]release.

如果一個對象使用了alloc,[mutable] copy,retain,那麼你必須使用相應的release或autonrelease

二、記憶體管理類型分類

基本類型和C語言的類型:如:

int,short,char,struct,enum,union等類型

OC類型:任何繼承於NSObject對象都屬於OC的類型。

我們講的記憶體管理實際上是對OC類型的記憶體管理,它對基礎資料型別 (Elementary Data Type)和C語言的類型並不管用。

三、C和C++記憶體管理的不足

如上,又3個引用指向了這一塊記憶體,任何一個調用了free方法釋放這個記憶體,而其餘的引用在不知道的情況下繼續使用這塊記憶體的時候,就會出現問題。何時由誰去釋放這塊記憶體,這就是C和C++在記憶體管理上的混亂。

四、OC對象在記憶體中的結構

所有的OC類型的對象的結構如下,這個對象的記憶體在包含自己的變數和方法的基礎上,還有一個包含retainCount的引用計數。

每一個OC對象都有一個4個位元組的retainCount的計數器。表示當前對象被引用的計數。如果計數為0,那麼就真正的去釋放這個對象。

 


 

規則:

1、Objective-C類中實現了引用計數器,對象知道自己當前被引用的次數

2、最初對象的計數器為1

3、如果需要引用對象,可以給對象發送一個retain訊息,這樣對象的計數器就加1

4、當不需要引用對象了,可以給對象發送release訊息,這樣對象計數器就減1

5、當計數器減到0,自動調用對象的dealloc函數,對象就會釋放記憶體

6、計數器為0的對象不能再使用release和其他方法

五、舉例說明

比如有一個引擎類Engine,有一個汽車類Car,Car裡面有一歌Engine的執行個體變數,一個setter和getter方法。具體如下

#import "Car.h"@implementation Car-(void)setEngine:(Engine*) engine{     _engine=engine;}-(Engine*)engine{    return _engine;}-(void)dealloc{    NSLog(@"Car is dealloc");    [super dealloc];}@end

上面寫的是一個簡單的類,當讓這樣寫是有問題,所以需要一步步的改進。

第一步改進:

先使用它看問題的所在,在main方法裡面如下使用:

        //先建立一個引擎        Engine* engine1=[[Engine alloc]init];        [engine1 setID:1];        //在建立一個汽車,設定汽車的引擎        Car* car=[[Car alloc]init];//retainCount=1        [car setEngine:engine1];        /*分析:在這裡,現在有兩個引用指向這個Engine對象,engine1和Car中的_engine,可是這個Engine對象的引用計數還為1,因為在         set方法中,並沒有使用retain。那麼不管是哪個引用調用release,那麼另外一個引用都會指向一塊釋放掉的記憶體,那麼肯定         會發生錯誤。所以需要在set方法中加以改進。*/

第二步改進:

setter方法改進

-(void)setEngine:(Engine*) engine{     _engine=[engine retain];//多了一個引用,retainCount+1}

再在main中使用它

        //先建立一個引擎        Engine* engine1=[[Engine alloc]init];        [engine1 setID:1];        //在建立一個汽車,設定汽車的引擎        Car* car=[[Car alloc]init];//retainCount=1        [car setEngine:engine1];//retainCount=2,因為使用了retain,所以retainCount=2,        //假設還有一個引擎        Engine* engine2=[[Engine alloc]init];        [engine2 setID:2];                //這個汽車要換一個引擎,自然又要調用settr方法                [car setEngine:engine2];                 /*分析:在這裡,汽車換了一個引擎,那麼它的_engine就不在指向engine1的哪個對象的記憶體了,而是換成了engine2,也就是說engine1的哪個對象指向的記憶體的引用只有一個       可是它的retainCount是兩個,這就是問題的所在了。所以仍然需要改進*/

第三步改進:

-(void)setEngine:(Engine*) engine{     [_engine release];//在設定之前,先release,那麼在設定的時候,就會自動將前面的一個引用release掉    _engine=[engine retain];//多了一個引用,retainCount+1}

再在main'中使用

        //先建立一個引擎        Engine* engine1=[[Engine alloc]init];        [engine1 setID:1];        //在建立一個汽車,設定汽車的引擎        Car* car=[[Car alloc]init];//retainCount=1        [car setEngine:engine1];//retainCount=2,因為使用了retain,所以retainCount=2,                //如果進行了一個誤操作,又設定了一次engine1              [car setEngine:engine1];                 /*分析:那麼,又要重新調用一次setter方法,這根本就是無意義的操作,浪費資源,所以要在設定之間加上判斷*/

第四步改進:

-(void)setEngine:(Engine*) engine{   if(_engine!=engine){//判斷是否重複設定           [_engine release];//在設定之前,先release,那麼在設定的時候,就會自動將前面的一個引用release掉           _engine=[engine retain];//多了一個引用,retainCount+1      } }

第五步:

現在setter方法基本沒有問題了,那麼在當我們要釋放掉一個car對象的時候,必須也要釋放它裡面的_engine的引用,所以,要重寫car的dealloc方法。

-(void)dealloc{    [_engine release]; //在釋放car的時候,釋放掉它對engine的引用    [super dealloc];}

這還不是最好的釋放的方法,下面的方法更好

-(void)dealloc{    [_engine setEngine:nil]; //在釋放car的時候,對setEngine設定為nil,它不僅會release掉,並且指向nil,即使誤操作調用也不會出錯。    [super dealloc];}

所以,綜上所述,在setter方法中的最終寫法是

-(void)setEngine:(Engine*) engine{   if(_engine!=engine){//判斷是否重複設定           [_engine release];//在設定之前,先release,那麼在設定的時候,就會自動將前面的一個引用release掉           _engine=[engine retain];//多了一個引用,retainCount+1      } }


然後在dealloc方法中寫法是:

-(void)dealloc{    [_engine setEngine:nil]; //在釋放car的時候,對setEngine設定為nil,它不僅會release掉,並且指向nil,即使誤操作調用也不會出錯。    [super dealloc];}


六、property中的setter文法關鍵字

在property屬性中有三個關鍵字定義關於展開setter方法中的文法,assgin(預設),retain,copy。當然這三個關鍵字是互斥的。

1、assgin展開stter的寫法

-(void)setEngine:(Engine*) engine{     _engine=engine;}

2、retain展開的寫法

-(void)setEngine:(Engine*) engine{   if(_engine!=engine){//判斷是否重複設定           [_engine release];//在設定之前,先release,那麼在設定的時候,就會自動將前面的一個引用release掉           _engine=[engine retain];//多了一個引用,retainCount+1      } }

3、copy展開的寫法

-(void)setEngine:(Engine*) engine{   if(_engine!=engine){//判斷是否重複設定           [_engine release];//在設定之前,先release,那麼在設定的時候,就會自動將前面的一個引用release掉           _engine=[engine copy];//多了一個引用,retainCount+1      } }

對於copy屬性有一點要主要,被定義有copy屬性的對象必須要符合NSCopying協議,並且你還必須實現了-(id)copyWithZone:(NSZone*)zone該方法。

可以看到,使用retain和我們上面舉得例子完全相同,所以我們可以使用property和它的retain代替之前的寫法。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.