天天看點

iOS程式設計基礎-OC(四)-記憶體管理

該系列文章系個人讀書筆記及總結性内容,任何組織和個人不得轉載進行商業活動!

上一節介紹了消息傳遞和消息轉發,接下來我們看看記憶體管理相關的内容;

 第4章 記憶體管理

     恰當的記憶體管理是正确而高效地開發程式的關鍵;

         本章詳細介紹為OC程式配置設定和釋放記憶體的途徑、OC的記憶體模型,以及如何編寫實作恰當記憶體管理的程式;

         計算機作業系統為程式配置設定有限記憶體,程式應該僅适用其必需的記憶體;

     OC語言及其運作時環境提供了支援應用記憶體管理的機制;

     4.1 程式的記憶體使用情況

     計算機記憶體中存儲和使用程式的方式:

         OC可執行程式是由(可執行)代碼、初始化和未初始化的程式資料、連結資訊、重定位資訊、局部資料和動态資料構成的;

         其中:

             程式資料包括靜态方式聲明的變量和程式常量(即在程式編譯時在代碼中設定的常數);

             可執行代碼、程式資料以及連結與重定位資訊會以靜态方式被配置設定記憶體,并在程式的聲明周期中一直存在;

             局部(自動)資料在語句中聲明并且隻在該語句中有效,語句執行後,局部資料不會繼續存在;

             從文法上來說,OC的複合語句塊就是又括号{}封裝的語句集合;

     自動資料被存儲在程式棧中,程式棧通常是在執行程式/線程前就被設定尺寸的記憶體段;

         棧用于存儲局部變量和調用方法/函數的上下文資料;

         上下文資料包括方法的輸入參數、傳回值,以及調用完方法後繼續執行程式的代碼位址;

         作業系統會自動管理這些記憶體;這些資料會獲得棧中的記憶體,而配置設定給這些資料的記憶體會在他們失效後被釋放;

     在運作時,OC會将建立的對象(通過NSObject的alloc方法建立的)存儲在動态配置設定的記憶體即堆記憶體中;

         以動态方式建立對象就意味着需要進行記憶體管理,因為在堆記憶體中建立的對象永遠不會超過其作用範圍;

     地位位址----------------------->高位位址

     可執行的二進制代碼->程式資料->堆->未使用的記憶體->棧->輸入程式的資料

     為程式代碼配置設定的記憶體是在編寫程式的時候設定的,是以占用的是系統記憶體;

     OC程式記憶體管理的必要性:

         一方面,程式的棧尺寸(通常)是在程式啟動時确定的,會自動由系統管理;

         另一方面,在OC中對象是在程式執行時動态建立的,不會有系統自動回收;

     不進行記憶體管理和錯誤的記憶體管理會導緻:

     1)記憶體洩漏:

         程式沒有釋放不再使用的對象,就會導緻該問題;繼續配置設定記憶體的話,最終會耗盡系統記憶體;

     2)懸挂指針:

         程式釋放了仍在使用的對象,會導緻該問題;如果将來程式嘗試通路這些對象,就會出現程式錯誤;

     4.2 OC的記憶體模型

     OC的記憶體管理是通過引用計數實作的;

         引用計數是一種通過對象的唯一引用,确定對象是否正在被使用的技術;

         如果對象的引用計數降到了0,對象就會被視為不再有用,而且運作時系統也會釋放它的記憶體;

     蘋果OC開發環境提供了兩種記憶體管理機制:

         手動管理(MRR)(也就是我們通常說的MRC,C是count的意思,是相對ARC來說的);

         自動引用計數(ARC);

     4.3 手動管理

     手動管理(MRR):是一種建立在對象所有權概念上的記憶體管理機制;

         隻要對象所有者還存在,對象就不會被OC運作時環境釋放;

         可以通過編寫代碼精确的管理對象的回收利用;

     我們先來了解下通路和使用對象的方式,以及通路對象與對象所有權之間的差别;

     4.3.1 對象引用和對象所有權

     OC對象是通過指向OC對象記憶體位址的變量,以間接方式通路的;

         這種變量就是C語言中的指針;

         指針的應用範圍很廣,包括OC的基本資料類型與C語言的資料類型;

         但,對象指針專門用于OC對象的互動操作;

     對象指針實作了OC對象的通路功能;

         但是,它們本身并不能管理所有權;

         比如:

             A * a = [[A alloc] init];

             A * b = a;

         聲明一個對象指針 ,指向另一個同類型的對象指針;我們雖然改變了指針的指向,但是并未設定原始對象的所有權;

             這會導緻,如果a被釋放了,b就指向了一個不合法的對象;

     要以MRR方式管理對象生命周期:即保留和釋放,在編寫代碼時需要遵守一系列記憶體管理規則;

     4.3.2 記憶體管理基本原則

     要正确使用MRR,編寫代碼時必須在擷取對象所有權與釋放對象所有權之間進行平衡;

     是以需遵循:

     1)為建立的所有對象設定所有權:

         應使用名稱以alloc、new、copy或mutableCopy開頭的方法建立OC對象;

         還應通過向塊發送copy消息,以動态的方式建立OC塊對象;

     2)應使用retain方法擷取對象(你尚未擁有)的所有權:

         使用NSObject的retain方法可以獲得對象的所有權;

         使用retain可以擷取你想要長時間使用的對象的所有權,這樣做通常可以将其存儲為屬性值,并防止其他操作無意中釋放該對象;

     3)當不再使用某個對象時,必須放棄其所有權:

         使用NSObject類的release和autorelease方法可以釋放對象的所有權;

         使用autorelease方法可以在目前自動釋放代碼塊的末尾,放棄對象的所有權;

         如果對象的引用計數為0,這兩種方法都會對對象執行dealloc方法;

     4)不能放棄不歸你所有的對象的所有權:

         這樣做會導緻過早滴釋放這些對象,如果程式嘗試方位已經被釋放的對象,就會出錯;

     注意,所有權是對象指針變量相對于對象來講的,對象一直都在,擷取和釋放的是對象指針變量對 對象的所有權;對象的所有權(用計數表示)清0時,相應的記憶體空間也會被運作時系統釋放;

     釋放操作:

     1)釋放記憶體:

         當對象的引用計數為0時,運作時系統會通過NSObject類的dealloc方法釋放掉該對象使用的記憶體;(注意了解這句話)

         該方法還提供了放棄子類對象所有權的架構:

             -(void)dealloc{

                 [... release];

                 ...

                 [super dealloc];

             }

         你編寫的類(通常都是NSObject類的子類)都應該重寫dealloc方法,調用它們執行個體變量的release方法,然後調用它們父類的dealloc方法;

         通過這種方式可以是你編寫的類,遵循類的層次結構以适當的方式放棄對象的所有權;

     2)通過autorelease方法延遲釋放操作:

         通過NSObject類的autorelease方法可以在自動釋放池代碼塊的末尾,調用對象中的方法;

         自動釋放代碼塊 提供了在将來某個時間放棄對象所有權的機制,因而無須編寫調用對象中release方法的具體代碼,并能避免對象立刻被釋放的情況;

         使用@autorelease指令可以定義自動釋放池代碼塊:

             @autorelease{

                 //建立自動釋放對象的代碼

                 ...

             }

         應該總是将建立自動釋放對象的代碼放在自動釋放池代碼塊中;

             否則他們将無法收到release消息,進而導緻記憶體洩漏;

         用于建立iOS和Mac OS X應用的蘋果應用架構,尤其是AppKit和UIKit,能夠自動提供自動釋放代碼塊;

         需要手動編寫自動釋放代碼塊的情況有以下幾種:

         (1)你編寫的程式不是以蘋果UI架構為基礎的,如指令行工具;

         (2)你實作的邏輯中含有建立很多臨時對象的循環;

             為了降低應用占用記憶體的最大值,你在該循環中添加自動釋放池代碼塊,以便在下一次疊代前處置這些對象;

         (3)你編寫的應用派生出來一個或多個輔助線程;

             你必須在執行輔助線程的位置添加自己編寫的自動釋放池代碼塊,否則你的應用就會記憶體洩漏;

     舉個例子:

         你可以向這樣寫:

         @autorelease{

             A * a = [[[A alloc] init] autorelease];

             ...

         }

     注意:

         上述對象在建立并初始化之後,會立即收到autorelease消息,這通常由一條複合語句實作;

         這種設計可以確定所有通過autorelease消息建立的對象都會在程式結束前、在自動釋放代碼塊的末尾被釋放;

     4.3.3 使用MRR

     示例:

         NSString * string1 = [NSString new];//new為建立的對象設定所有權

         NSString * string2 = string1;   //對象指針變量指派

         [string2 retain];               //對象指針變量使用retain擷取對象所有權

         AClass * a = [[AClass alloc] init];//alloc為建立的對象設定所有權

         [string1 release];

         [string2 release];              //對象指針變量使用release放棄對象所有權

         [a release];

     以上示例展示了 以動态方式建立對象時,調動初始化和釋放方式的順序;

         所有對象的建立/保留和釋放消息都達到了平衡;

     Xcode的Product菜單的Analyze選項:該工具會分析程式,檢測潛在的記憶體洩漏,懸挂指針等問題;(Commend + shift + B)

     當然也可以用Xcode Instrument工具進行記憶體使用情況的分析;

     下一節介紹自動引用計數的使用;

繼續閱讀