iOS開發日記53-ARC和記憶體管理,ios日記53-arc

來源:互聯網
上載者:User

iOS開發日記53-ARC和記憶體管理,ios日記53-arc

今天博主有一個記憶體管理的需求,遇到了一些困痛點,在此和大家分享,希望能夠共同進步.

由於行動裝置的記憶體有限,所以我們需要對記憶體進行嚴格的管理,以避免記憶體泄露造成資源浪費。在OC中,只有對象才屬於記憶體管理範圍,例如int、struce等基礎資料型別 (Elementary Data Type)不存在記憶體管理的概念。在iOS開發中,對記憶體的管理實際上就是對引用計數器的管理。

OC記憶體管理的三種方式自動垃圾收集

在OC2.0中,有一種自動垃圾收集的記憶體管理形式,通過垃圾自動收集,系統能夠自動檢測出對象是否擁有其他的對象,當程式運行期間,不被引用的對象就會自動釋放。
說明:在iOS運行環境中不支援自動垃圾收集,在OS X環境才支援,但是Apple現在不建議使用該方法,而是推薦使用ARC進行替代。

手動引用計數器(MRC)和自動釋放池;引用計數器的概念

顧名思義,引用計數器即一個對象被引用(使用)的次數,每個對象的引用計數器佔用4個位元組
如所示,當使用A建立一個對象object的時候,object的RC預設為1,當B也指向object的時候,object的RC+1=2。然後A指標不指向object的時候,object的RC-1=2-1=1。最後當B也不指向object的時候,object的RC-1=1-1=0,此時對象object被銷毀。
說明: 當一個對象被建立的時候,該對象的RC預設為1;當該對象被引用一次,需要調用retain方法,使RC的值+1;當指標失去對該對象的引用,需要調用release方法,使RC的值-1;當RC=0的時候,該對象被系統自動銷毀回收。

手動引用計數器(MRC)

MRC即我們通過人為的方式來控制引用計數器的增減,影響對象RC值得方法有以下幾種:

//Book類的聲明和實現@interface Book:NSObject@end@implementation Book@end//Peron類的聲明和實現@interface Person:NSObject{    Book *_book;}- (void)setBook:(Book *)book;@end@implementation Person- (void)setBook:(Book *)book{    if(_book != book)    {//如果新設定的book對象不是之前指向的book對象          [_book release];//使之前對象的RC-1          _book = [book retain];//當前引用的RC+1      }- (void)dealloc //重載dealloc方法銷毀對象{    [_book release];//由於_book控制了Book對象,_book調用release方法使RC-1    [super dealloc];//調用父類的dealloc方法,而且必須放在最後一行}}@end//主函數測試void main(){    Book *b=[Book new];    Person *p = [Person new];    p.book = b;    [b release];//b控制了Book對象,b調用release方法使RC-1    [p release];//p控制了Person對象,p調用release方法使RC-1}

通過上面代碼知道,成員變數的設值和取值方法是手動產生的,而且setter方法中成員變數的引用計數器也是手動設定,我們也可以通過@property以及相應關鍵字來由編譯器產生。
關於在MRC中@property關鍵字如下:
1. assign和retain和copy
這幾個關鍵字用於setter方法的記憶體管理,如果使用assign(一般用於非OC對象),那麼將直接執行賦值操作;如果使用retain(一般用於OC對象),那麼將retain新值,release舊值;如果使用copy,那麼將release舊值,copy新值。不顯示使用assign為預設值
2. nonatomic和atomic
這兩個關鍵字用於多線程管理,nonatomic的效能高,atomic的效能低。不顯示使用atomic為預設值
3.readwrite和readonly 
這兩個關鍵字用於說明是否產生setter方法,readwrite將自動產生setter和getter方法,readonly 只產生getter方法。不顯示使用readwrite為預設值
4. getter和setter
這兩個關鍵字用於給設值和取值方法另外起一個名字。例如@property(getter=a,setter=b:) int age;相當於取值方法名為a,設值方法名為b:。
如果使用@property屬性,那麼上面代碼可以改為:

//Book類的聲明和實現@interface Book:NSObject@end@implementation Book@end//Peron類的聲明和實現@interface Person:NSObject- (void)dealloc;@property(nonatomic,retain) Book *_book;@end@implementation Person- (void)dealloc //重載dealloc方法銷毀對象{    [_book release];//用於_book控制了Book對象,_book調用release方法使RC-1    [super dealloc];//調用父類的dealloc方法,而且必須放在最後一行}}@end//主函數測試void main(){    Book *b=[Book new];    Person *p = [Person new];    p.book = b;    [b release];//b控制了Book對象,b調用release方法使RC-1    [p release];//p控制了Person對象,p調用release方法使RC-1}
循環參考記憶體管理原則

對於兩個類A包含B,B包含A的循環參考情況下,看如下代碼:

//Book1類的聲明和實現@interface Book1:NSObject@property(nonatomic,retain) Book2 *_book2;- (void)dealloc;@end@implementation Book1- (void)dealloc //重載dealloc方法銷毀對象{    [_book2 release];//用於_book控制了Book對象,_book調用release方法使RC-1    [super dealloc];//調用父類的dealloc方法,而且必須放在最後一行}@end//Book2類的聲明和實現@interface Book2:NSObject@property(nonatomic,retain) Book1 *_book1;- (void)dealloc;@end@implementation Book2- (void)dealloc //重載dealloc方法銷毀對象{    [_book1 release];//用於_book控制了Book對象,_book調用release方法使RC-1    [super dealloc];//調用父類的dealloc方法,而且必須放在最後一行}}@end//主函數測試void main(){    Book1 *b1=[[Book1 alloc] init];    Book2 *b2=[[Book2 alloc] init];    b1.book2 = b2;    b2.book1 = b1;    [b1 release];    [b2 release];}

下面分析主函數代碼,當執行Book1 *b1=[[Book1 alloc] init]後,b1指向Book1。當執行Book2 *b2=[[Book2 alloc] init]後,b2指向Book2。 當執行b1.book2 = b2後,Book1的成員變數_b2指向Book2。當執行b2.book1 = b1後,Book2的成員變數_b1指向Book1。記憶體中具體關係如所示。

此時Book1的引用計數器RC=2,Book2的引用計數器RC=2。
當執行 [b1 release]後,b1釋放對Book1的控制權,此時Book1的引用計數器RC=2-1=1。
當執行[b2 release]後,b2釋放對Book2的控制權,此時Book2的引用計數器RC=2-1=1。
那麼由於仍有指標指向Book1和Book2,這時記憶體中Book1和Book2的關係如黑色橢圓內所示。所以並不會調用dealloc函數,所以Book1和Book2並不會毀銷,這樣就造成了 記憶體泄露


對於上面這種情況,只需要在Book1和Book2的@property屬性聲明中 一端使用retain,一端使用assign。即將@property(nonatomic,retain) Book1 *_book1或者@property(nonatomic,retain) Book2 *_book2中的一個retian改為assign。具體原因自己分析。

看下面這種循環參考情況,只能使用assign

@interface Book:NSObject@property(nonatomic,assign)id  instance;  //此處必須用assign- (void)dealloc;@end@implementation Book- (void)dealloc //重載dealloc方法銷毀對象{    [_instance release];//用於_book控制了Book對象,_book調用release方法使RC-1    [super dealloc];//調用父類的dealloc方法,而且必須放在最後一行}@endvoid mian(){    Book b1 = [[Book alloc] init];    Book b2 = [[Book alloc] init];    b1.instance = b2;    b2.instance = b1;    [b1 release];    [b2 release];}

大家可以分析一下,如果@property(nonatomic,assign)id instance; 中將assign換為retain,那麼也將造成記憶體泄露。

Autorelease Pool的使用

顧名思義,autorelease即自動釋放對象,不需要我們手動釋放。從上面代碼我們知道,在主函數中,建立對象obj後,總要手動調用[obj release]方法,這樣無疑使工作量變大,且對我們的技術增長毫無意義。為了減少這種無意義的工作,可以使用Autorelease Pool方式。
Autorelease Pool即自動釋放池,在Autorelease Pool內的對象在建立時只要調用了autorelease方法,那麼在該池子內的對象的最後的release方法的調用將由編譯器完成。

Autorelease Pool的建立有兩種方式:

1.通過@autoreleasepool方法建立,如下:@autoreleasepool{//在大括弧內建立的對象最後不需要手動調用release方法。}
2. 通過NSAutoreleasePool類建立,如下:NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]  init];//此範圍為自動釋放池[pool release];

Autorelease Pool的使用例子如下:

void main(){     @autoreleasepool    {        Person p = [[[Person alloc] init] autorelease]; //調用autorelease方法        //不需要再調用[p release];方法,超過autoreleasepool範圍會自動調用該方法。    }    }

在返回對象的方法中最好使用自動釋放池釋放對象,因為如果將新建立的對象作為傳回值,由於在返回該對象之前並不能釋放該對象,所以可以通過自動釋放池來延遲該對象的釋放。範例程式碼如下:

-(Person *)getNewPerson{    Person p* = [[[Person alloc] init] autorelease];    //do something    return p;}或者{    Person p* = [[Person alloc] init] ;    //do something    return [p autorelease];}

注意點:

自動引用計數器(ARC)

ARC將由編譯器來自動完成對象引用計數器的控制,不需要手動完成。
ARC模式下,建立的新對象通常由以下幾種關鍵字來限定。

在ARC模式下,MRC中的retain、release等方法變的不可用,因為ARC是不需要我們手動管理記憶體的,一切由編譯器完成。
MRC模式下,將一個對象指標賦值給另一個對象指標如下:

Person p1 = [Person new];Person p2 = [Person new];[p2 release]//在p2失去對對象的控制權時需要先releasep2 = p1;//進行賦值操作

但是在ARC模式下,我們完全可以不關心具體怎麼操作,只需要直接進行賦值即可:

Person p1 = [Person new];Person p2 = [Person new];p2 = p1;//進行賦值操作
ARC模式下的循環參考

在ARC模式下,@property屬性關於記憶體管理的修飾符為strong和weak(MRC下的retain和assign不可用),表示聲明為強指標還是弱指標。通常情況下都是使用strong來修飾,但是在循環參考卻不是。

下面這種情況一端使用strong修飾,一端使用weak修飾。如果都使用strong修飾,那麼將造成對象的迴圈保持,造成記憶體泄露。

//Book1類的聲明和實現@interface Book1:NSObject@property(nonatomic,strong) Book2 *_book2;@end@implementation Book1@end//Book2類的聲明和實現@interface Book2:NSObject@property(nonatomic,weak) Book1 *_book1;@end@implementation Book2@end//主函數測試void main(){    Book1 *b1=[[Book1 alloc] init];    Book2 *b2=[[Book2 alloc] init];    b1.book2 = b2;    b2.book1 = b1;}

下面這種循環參考情況,只能使用weak。如果使用strong修飾,那麼將造成對象的迴圈保持,造成記憶體泄露。

@interface Book:NSObject@property(nonatomic,weak)id  instance;  //此處必須用assign@end@implementation Book@endvoid mian(){    Book b1 = [[Book alloc] init];    Book b2 = [[Book alloc] init];    b1.instance = b2;    b2.instance = b1;}

注意點:

總結

記憶體管理的本質是對對象引用計數器的操作,理解MRC模式下記憶體管理操作有助於我們對OC記憶體管理的理解。記憶體管理只針對對象而言,注意MRC和ARC下@property屬性關鍵字的選擇,在MRC模式下,OC對象通常使用retain關鍵字,非OC對象使用assign關鍵字,但是循環參考是一個例外,通常需要一端使用assign,一端使用retain;在ARC模式下,OC對象通常使用strong關鍵字,非OC對象使用assign關鍵字,但是循環參考是一個例外,通常需要一端使用strong,一端使用weak

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.