1.什麼是記憶體管理 行動裝置的記憶體極其有限,每個app所能佔用的記憶體是有限制的當app所佔用的記憶體較多時,系統會發出記憶體警告,這時得回收一些不需要再使用的記憶體空間。比如回收一些不需要使用的對象、變數等管理範圍:任何繼承了NSObject的對象需要去管理記憶體,但是對於對其他基礎資料型別 (Elementary Data Type)(int、char、float、double、struct、enum等)結構,枚舉等不用去關心記憶體複製代碼- (void)test{ int a = 20; int b = 10; Person *person = [[Person alloc] init]; // 方法未退出之前 這三行代碼在記憶體中}複製代碼 一旦test方法執行完畢,意味著局部變數的範圍也失效,那麼棧空間的局部變數系統會自動檢測回收。但是堆空間中動態產生的對象是還沒有被回收。 可以看出即使已經沒有指標指向動態產生的對象了,但還是沒有被回收,因此需要手動管理釋放。釋放的方法是為對象發送一條訊息。因此需要調用對象的某個方法來釋放對象。 那麼系統是怎麼知道此時的對象需不需要回收呢?這就涉及到了對象結構中的"引用計數" 2.對象結構 每個OC對象內部都有自己的引用計數器,它是一個整數,表示"對象被引用的次數",即有多少人正在使用這個OC對象每個OC對象內部會自動化佈建4個位元組的儲存空間來儲存引用計數器 3.引用計數器的作用 當使用alloc、new或者copy建立一個新對象時,新對象的引用計數器預設就是1當一個對象的引用計數器值為0時,對象佔用的記憶體就會被系統回收。換句話說,如果對象的計數器不為0,那麼在整個程式運行過程,它佔用的記憶體就不可能被回收,除非整個程式已經退出 4.操作對象引用計數器的方法 給對象發送一條retain訊息,可以使引用計數器值+1(retain方法返回對象本身)retain方法返回的是id類型,那麼哪個對象調用返回的就是自己給對象發送一條release訊息,可以使引用計數器值-1可以給對象發送retainCount訊息獲得當前的引用計數器值 5.對象的銷毀 當一個對象的引用計數器值為0時,那麼它將被銷毀,其佔用的記憶體被系統回收當一個對象被銷毀時,系統會自動向對象發送一條dealloc訊息一般會重寫dealloc方法,在這裡釋放相關資源,dealloc就像對象的遺言一旦重寫了dealloc方法,就必須調用[super dealloc],並且放在最後面調用不能直接調用dealloc方法一旦對象被回收了,它佔用的記憶體就不再可用,堅持使用會導致程式崩潰(野指標錯誤) 複製代碼 // alloc方法是給堆中分配記憶體 init方法和記憶體無關 此時retainCurrent為1 Person *p = [[Person alloc] init]; // 返回的就是對象本身 retainCurrent為2 [p retain]; // retainCurrent為1 [p release]; // retainCurrent為0 說明Person類對象被回收,那麼對應的在記憶體中的地址已經不可用了 此時的Person對象稱為“殭屍對象”但是此時p指標還是在指向Person類對象所對應的那塊停用地址此時的p指標稱為“野指標” [p release];複製代碼 6.開發中要注意的記憶體管理 預設情況下,Xcode是不會管理殭屍對象的,即使使用了一塊被釋放的記憶體也不會報錯。為了方便調試,應該開啟殭屍對象監控。設定: 注意三個概念: 殭屍對象:已經被回收的對象,或者說對象所對應的記憶體位址已經停用對象稱為殭屍對象。殭屍對象不可用 野指標:指向一塊不可用記憶體地址或者指向殭屍對象的指標稱為野指標。給野指標發送訊息會報 EXC_BAD_ACCESS錯誤 null 指標:沒有指向任何指標變數稱為空白指標,也意味著指標變數所儲存的值為0,nil,NULL 這樣可以避免野指標錯誤的發生 複製代碼/********************************** Person.h **************************************/#import <Foundation/Foundation.h> @interface Person : NSObject @property int age; @end /********************************** Person.m **************************************/#import "Person.h" @implementation Person // 重寫父類NSOjbct的遺言方法 對象在被釋放之前一定會調用dealloc方法 - (void)dealloc{ NSLog(@"對象在釋放之前會執行遺言方法被執行"); [super dealloc]; // 一定要調用 而且必須放在最後面}@end /********************************** main.m **************************************/#import <Foundation/Foundation.h>#import "Person.h" /* main方法是一個死迴圈方法以保證程式能持續運行,除非使用者關閉程式或者是手機沒電,程式才能終止 那麼在main方法裡面的Person對象不就一直存在麼,因此必須在main方法裡面將對象回收 */int main(int argc, const char * argv[]){ // alloc方法是給堆中分配記憶體 init方法和記憶體無關 此時retainCurrent為1 Person *p = [[Person alloc] init]; // 返回的就是對象本身 retainCurrent為2 [p retain]; // retainCurrent為1 [p release]; /* retainCurrent為0 說明Person類對象被回收,那麼對應的在記憶體中的地址已經不可用了 此時的Person對象稱為“殭屍對象” 此時p指標還是在指向Person類對象所對應的那塊停用地址,此時p指標稱為“野指標” */ [p release]; /* 對象已經被回收,千萬別以為再給對象發送一個retain訊息對象就可以“起死回生”應該節哀順變 執行retain方法會報錯,此時的p指標已稱為野指標執行代碼回報:野指標錯誤 */ // [p retain]; /* 此時對象已經被回收稱為“殭屍對象了”不可以再訪問屬性 在執行p.age = 10;報錯: -[Person setAge:]: message sent to deallocated instance 訊息發送給了已經被釋放的對象 再次證明“殭屍對象不可以用” */ /* 一旦指標成為野指標再繼續向p指標所指的對象發送訊息就會報錯:Exc_BAd_ACCESS 說明訪問了一塊壞記憶體(已經被回收、停用記憶體) “野指標錯誤” 那麼此時在對象回收之後將指標變數清空 那麼棧中的指標變數就不會再指向堆中類對象的記憶體位址了 */ p = nil; /* 指標變數內部所儲存的值已被清空,那麼指標已經無指向 再給指標發送任何訊息指標會無任何響應,而且也不報錯因為OC中沒有null 指標錯誤 */ [p release]; [p release]; [p release]; [p release]; return 0;