iOS-ARC機制,iosarc原理

來源:互聯網
上載者:User

iOS-ARC機制,iosarc原理

記憶體管理是開發軟體中重要的一個課題。如果記憶體管理不當,輕者記憶體泄露,重者程式崩潰。

下面重要講述一下iOS的ARC(Automatic Reference Counting))機制。

ARC的曆史由來

在iOS1.0的時候,蘋果公司沒有開放手機開發介面,不存在iOS開發人員這個概念。

在iOS2.0的時候,我們管理記憶體使用量的技術數MRC機制。

在iOS5.0的時候,蘋果公司推出了ARC機制。

我們知道在MRC管理記憶體的時候,我們需要遵守“誰建立,誰釋放,誰引用,誰管理”這個黃金法則,我們在建立對象的時候,需要考慮在什麼時候釋放記憶體,有時候對記憶體管理機制不熟悉的話,就很有可能導致記憶體泄露和過度釋放的問題。

手動管理記憶體會導致很多問題的產生,在iOS5.0的時候,蘋果推出了自動記憶體管理ARC,當時使用的IDE是XCode4.0,在我們建立工程的時候,下面有一個選項,是否使用ARC,給開發人員一個自主選擇,但是推出ARC,去很少人去使用ARC,在XCode5.0的時候,已經不存在這個選項,我們建立工程預設選擇的是ARC機制,我們我們想要使用MRC,需要手動設定工程。

在推出ARC的時候,很多人都說iOS的ARC和Android的GC機制很像,但是他們本身還是有很大的區別的。其中GCS是運行時特性,ARC是編譯時間特性。

ARC的使用

ARC我們字面翻譯是自動引用計數,引申意我們可以理解為自動記憶體管理。自動記憶體管理難道我們真的不需要管理了嗎?非也。ARC機制也會導致記憶體泄露的問題,我們在使用的時候,需要注意這些問題。

我們在MRC中,我們經常要使用release,autorelease,retain這些關鍵字,來保留引用計數或者釋放對象。在ARC中,我們就不能這麼處理了。如果我們使用這些關鍵字,我們的程式在基本的編譯都不能通過。

既然我們建立的對象,不用我們手動釋放,系統在必要的時候會為我們釋放,那麼對象會在什麼時候釋放掉呢?

探討這個問題之前,我們先說明一下使用ARC的基本準則。

  • 強引用指向的對象不會被釋放。
  • 一個對象沒有強引用會立刻釋放。
  • 弱引用指向的對象將要釋放時自動為空白。

注意:我們建立的對象預設是強引用,比如:People = [People new];等價__strong People = [People new];

下面我們講述的內容都圍繞著這三個準則。

一、局部對象

首先建立一個工程,然後添加一個類People,下面為People的.m檔案內容。

 

1 @implementation People 2 -(id)init 3 { 4 if (self = [super init]) 5 { 6 NSLog(@"%s",__FUNCTION__); 7 } 8 return self; 9 }10 -(void)dealloc11 {12 NSLog(@"%s",__FUNCTION__);13 }14 @endPeople.m

 

我們在viewDidLoad中添加People *p = [People new]; NSLog(@"%s",__FUNCTION__);

列印結果為:

2015-10-19 13:16:10.589 textarc[2627:93432] -[People init]
2015-10-19 13:16:10.600 textarc[2627:93432] -[ViewController viewDidLoad]
2015-10-19 13:16:10.600 textarc[2627:93432] -[People dealloc]

我們發現People這個對象別釋放,從列印順序上我們可以看到是在viewDidLoad執行完畢之後這個People對象被釋放掉的。

局部對象為什麼會在函數執行完畢之後被釋放掉呢?

在viewDidLoad中,p是一個強引用,對象不會被釋放,會列印NSLog中的內容。但是在函數執行完畢之後,強引用指標不在指向對象。根據上面的準則,沒有強指標指向對象會被立刻釋放,所以在執行完viewDidLoad之後,對象People會被釋放掉。

如果我們在viewDidLoad中這樣寫:

__weak People *p = [People new];
NSLog(@"%s",__FUNCTION__);

我們可以猜測到列印結果就會跟上面的不同

2015-10-19 13:22:10.783 textarc[2682:96983] -[People init]
2015-10-19 13:22:10.783 textarc[2682:96983] -[People dealloc]
2015-10-19 13:22:10.784 textarc[2682:96983] -[ViewController viewDidLoad]

因為對象沒有強指標引用,所以People會被釋放,然後執行下面的列印。

二、全域變數

現在我們將People定義成全域對象,命名為_people。

我們在viewDidLoad中寫下面代碼:

 _people = [People new];
 NSLog(@"%s",__FUNCTION__);

列印結果為:

2015-10-19 13:28:32.076 textarc[2764:100790] -[People init]
2015-10-19 13:28:32.077 textarc[2764:100790] -[ViewController viewDidLoad]

我們發現並沒有調用People的dealloc方法,因為在執行完viewDid這個函數時,還有一個強引用指標指向People,根據上面的準則,對象不會被釋放。

如果我們在全域對象前面加上一個__weak:   __weak People *_people;

再執行上面的代碼,列印結果為:

2015-10-19 13:31:47.816 textarc[2803:102843] -[People init]
2015-10-19 13:31:47.827 textarc[2803:102843] -[People dealloc]
2015-10-19 13:31:47.828 textarc[2803:102843] -[ViewController viewDidLoad]

出現這個結果不用解釋了吧,雖然是一個全域的對象,但是是弱引用,沒有強引用,對象會被釋放掉。

 

三、全域和局部混合使用

現在我們定義一個全域行強指標People的對象p,同時建立一個局部性強指標對象p1.

viewDidLoad中為:

People *p1 = [People new];
p = p1;

執行結果為:

2015-10-19 13:49:32.380 textarc[3088:111876] -[People init]
2015-10-19 13:49:32.383 textarc[3088:111876] -[ViewController viewDidLoad]

因為p1是局部強引用,在函數執行完後按常理是被釋放掉,但是在釋放前,有一個全域性的強引用執行了它,所有People沒有被釋放掉。

如果我們在viewDid中這樣寫:

__weak People *p1 = [People new];
p = p1;

NSLog(@"%s",__FUNCTION__);

列印結果為:

2015-10-19 13:50:43.647 textarc[3115:112734] -[People init]
2015-10-19 13:50:43.651 textarc[3115:112734] -[People dealloc]
2015-10-19 13:50:43.651 textarc[3115:112734] -[ViewController viewDidLoad]

因為局部對象是弱引用,對象會被釋放掉,在賦值給全域強引用之前,它已經為空白了,對象已經釋放掉了。

四、建立對個對象嵌套

假設我們還有一個類,類名為Car,People有一個屬性,@property(nonatomic,strong) Car *car;

現在我們把People聲明一個全域對象p.

在viewDidLoad中:

p = [People new];
Car *c = [Car new];
p.car = c;
NSLog(@"%s",__FUNCTION__);

列印結果:

2015-10-19 13:57:21.883 textarc[3220:116572] -[People init]
2015-10-19 13:57:21.886 textarc[3220:116572] -[Car init]
2015-10-19 13:57:21.886 textarc[3220:116572] -[ViewController viewDidLoad]

Car雖然是局部引用,但是函數執行完畢後沒有釋放,因為p對其還有一個強引用,所有Car不會被釋放掉。

如果我們將People釋放掉,Car自然也會釋放。

我們想要釋放一個一個變數的話,我們可以直接將變數賦值為空白,就會釋放掉。

例如:

p = [People new];
Car *c = [Car new];
p.car = c;
p = nil;
NSLog(@"%s",__FUNCTION__);

列印結果為:

2015-10-19 14:01:14.703 textarc[3265:118993] -[People init]
2015-10-19 14:01:14.706 textarc[3265:118993] -[Car init]
2015-10-19 14:01:14.706 textarc[3265:118993] -[People dealloc]
2015-10-19 14:01:14.706 textarc[3265:118993] -[ViewController viewDidLoad]
2015-10-19 14:01:14.707 textarc[3265:118993] -[Car dealloc]

五、循環參考問題

加入在People中有Car這個屬性,在Car中有People這個屬性。(注意交叉引用問題)

我們在viewDidLoad中:

People * p = [People new];
Car *c = [Car new];
p.car = c;
c.people = p;
NSLog(@"%s",__FUNCTION__);

結果為:

2015-10-19 14:05:50.296 textarc[3320:121202] -[People init]
2015-10-19 14:05:50.298 textarc[3320:121202] -[Car init]
2015-10-19 14:05:50.298 textarc[3320:121202] -[ViewController viewDidLoad]

我們發現都沒有釋放,會導致記憶體泄露。當然我們可以將c,或者p賦值為nil將其釋放掉。但是這樣寫很糟糕,我們需要判斷什麼釋放決定釋放。

如何解決這個問題呢?

在屬性修飾的時候,不要同時使用strong,可以一個使用weak修飾,則運行結果如下:

2015-10-19 15:45:53.289 textarc[3937:150043] -[People init]
2015-10-19 15:45:53.298 textarc[3937:150043] -[Car init]
2015-10-19 15:45:53.298 textarc[3937:150043] -[ViewController viewDidLoad]
2015-10-19 15:45:53.298 textarc[3937:150043] -[People dealloc]
2015-10-19 15:45:53.298 textarc[3937:150043] -[Car dealloc]

 

 ARC總結在使用ARC的時候,可以在一定程度上簡化我們的編程操作,但是在使用的過程中也會出現記憶體泄露的問題,需要我們在實際使用過程中總結出現問題的情況,讓我們的程式有更少的bug和潛在的bug。    

相關文章

聯繫我們

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