Object-C學習筆記——記憶體管理
Object-c記憶體管理方式有三種:手動引用計數(MRC,Manual Reference Count)、自動引用計數(ARC,Automatic Reference Count)、自動垃圾回收。
1.MRC是預設的記憶體管理方式。就使用的難易度上說還是較難的,稍不注意可能就導致了記憶體洩漏(什麼是記憶體洩漏呢,記憶體洩漏是指程式未能釋放不再使用的記憶體),如alloc和release沒有成對使用(常見的現象是使用迴圈時用了break、goto、continue在一定條件下跳出迴圈,而release語句在break、goto、continue之後,導致沒有執行release,從而出現記憶體洩漏);
2.ARC在IOS5之後被強烈建議使用。為什麼呢?因為編譯器會根據傳入變數是引用變數還是區域性變數,返回物件的方法是不是初始化方法等資訊來推斷何處應該自動新增autorelease/retain/release,不需要程式設計師手動新增,從而使程式設計師有更多時間去開發程式的核心功能。
3.自動垃圾回收是IOS不支援的(Mac支援)。今天在這裡就不暫時介紹了;
那引用計數的基本思想或者說基本原理是什麼呢?
首先,我們建立一個物件會執行alloc,為這個物件分配一個記憶體空間。誒,既然分配了空間,我們自然要在程式執行完畢時釋放這個記憶體空間,否則就造成記憶體洩漏了。那怎麼讓系統知道要在這時候釋放這個例項物件的記憶體空間呢?採用標記咯,則這個引用計數我就把他理解為一個“標記符”。
我們在執行alloc和初始化方法是,引用計數就會+1(從0變為1)。此時系統分配了記憶體,“標記符”就不是零了。假設一個物件A會呼叫物件B,為了不讓B隨意釋放,A就需要執行retain操作,使B的引用計數+1,宣佈A是B的擁有者。即告訴系統,物件B是我的,你不可以隨意釋放。在A使用完B之後,A向B傳送release訊息,表示宣佈B不再是我的人了,此時B的引用計數就會-1,如果剛好這個值-1=0,那麼B就會被釋放了。注意:同一個物件可以有多個擁有者,每個擁有者宣佈擁有他時都必須要執行retain。這樣可以保證這個物件在另一個物件需要呼叫它之前不被系統釋放。
我們如果想要知道這個實際的引用計數值是多少,就會用到retainCount方法。示例程式碼如下:
#import <Foundation /NSObject.h>
#import<stdio.h>
int main(){
id obj=[[NSObject alloc] init];
printf("執行初始化後,引用計數為:%d\n",(int) [obj retainCount]);
[obj retain];
printf("執行retain後,引用計數為:%d\n",(int) [obj retainCount]);
[obj release];
printf("執行release後,引用計數為:%d\n",(int) [obj retainCount]);
[obj release];
return 0;
}
執行完後輸出結果是:
執行初始化後,引用計數為:1
執行retain後,引用計數為:2
執行release後,引用計數為:1
注意:alloc、init與release,retain與release必須要成對出現。
引用計數為0時,系統會自動呼叫dealloc方法,釋放記憶體。通常程式內是不允許直接呼叫這個方法的。
嗯,現在我們基本清楚了引用計數記憶體管理的基本思想,而這種基本思想必然是會擴充套件的。實際編寫程式時,我們會遇到很多隻需要使用一次的物件,而逐一的釋放這些物件會顯得很麻煩。於是呢,Cocoa環境的Object-C提供了一種自動釋放機制。這個機制是怎樣的呢?從功能上來認識,它要實現的目的很簡單,就是將這些使用次數少且使用完後的物件統一的釋放。那麼很顯然,它首先要做的就是把這些物件集中起來,而集中的容器就應運而生——AtuoreleasePool(自動釋放池)。同樣使用標記的方式,用類NSAutoreleasePool來記錄。這種機制的使用方法如下:
1.建立一個NSAutoreleasePool的例項物件;
2.當這個物件需要被釋放時用autorelease替代release,將這個物件標記(標記為以後釋放)後放入自動釋放池;
3.銷燬自動釋放池,池中記錄的所有物件都被髮送release訊息,然後就被統一釋放了。
這裡需要注意的是,autorelease方法雖然不會使物件的引用計數發生變化,但是它和release一樣都宣佈放棄物件所有權。autorelease也要和retain成對出現。
這些被記錄到自動釋放池中的物件稱為臨時物件。這種物件可以直接建立,在建立後就會被直接加入到內部的自動釋放池。如+(id) stringWithUTF8String:(const char *) bytes,
這就是個生成臨時變數的類方法(不以init開頭而已生成物件型別作為開頭的方法),O-C中它就叫做便利建構函式。以上都是手動引用計數記憶體管理的方法。
手動管理自動釋放池使用方法如下,
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
/*進行一系列操作*/
/*這裡不可以使用break、continue、goto語句*/
[pool release];
自動管理自動釋放池,程式碼變為:
@autoreleasepool{
/*進行一系列操作*/
/*這裡可以使用break、continue、goto語句*/
}
為什麼後一種方法可以使用break、continue等語句呢?因為程式運動到@autoreleasepool塊外才會進行物件釋放,所以你在塊內怎麼continue,怎麼goto都沒關係。我想這也是為什麼在ios5之後,強烈推薦使用ARC機制。採用ARC必定是讓我們程式設計師少了很多工作,但使用它時也有以下幾點注意事項:
1.使用ARC記憶體管理時,不能在程式中新增retain、release、autorelease和retainCount;
2.要使用@autoreleasepoo代替NSAutoreleasePool;
3.方法命名必須依照命名規則,不能隨意定義以new/copy/alloc/init/mutablecopy開頭且和所有權操作無關的方法;
4.不用再dealloc中釋放例項變數,也不需要呼叫[super dealloc];
5.編譯時使用clang編譯器,並加上編譯選項-fobjc-arc;
最後說一點,在Xcode中,我們可以在選單中選中Edit-Refactor,找到一個叫Convert to Object-C ARC的小工具。這個工具可以將程式碼轉化為支援ARC的程式碼。但垃圾回收的程式碼不能被自動轉換。