iOS/OS X記憶體管理(一):基本概念與原理
在Objective-C的記憶體管理中,其實就是引用計數(reference count)的管理。記憶體管理就是在程式需要時程式設計師分配一段記憶體空間,而當使用完之後將它釋放。如果程式設計師對記憶體資源使用不當,有時不僅會造成記憶體資源浪費,甚至會導致程式crach。我們將會從引用計數和記憶體管理規則等基本概念開始,然後講述有哪些記憶體管理方法,最後注意有哪些常見記憶體問題。
memory management from apple document
基本概念
引用計數(Reference Count)
為了解釋引用計數,我們做一個類比:員工在辦公室使用燈的情景。
引用Pro Multithreading and Memory Management for iOS and OS X的圖
當第一個人進入辦公室時,他需要使用燈,於是開燈,引用計數為1
當另一個人進入辦公室時,他也需要燈,引用計數為2;每當多一個人進入辦公室時,引用計數加1
當有一個人離開辦公室時,引用計數減1,當引用計數為0時,也就是最後一個人離開辦公室時,他不再需要使用燈,關燈離開辦公室。
記憶體管理規則
從上面員工在辦公室使用燈的例子,我們對比一下燈的動作與Objective-C物件的動作有什麼相似之處:
燈的動作 Objective-C物件的動作
因為我們是通過引用計數來管理燈,那麼我們也可以通過引用計數來管理使用Objective-C物件。
引用Pro Multithreading and Memory Management for iOS and OS X的圖
而Objective-C物件的動作對應有哪些方法以及這些方法對引用計數有什麼影響?
當你alloc一個物件objc,此時RC=1;在某個地方你又retain這個物件objc,此時RC加1,也就是RC=2;由於呼叫alloc/retain一次,對應需要呼叫release一次來釋放物件objc,所以你需要release物件objc兩次,此時RC=0;而當RC=0時,系統會自動呼叫dealloc方法釋放物件。
Autorelease Pool
在開發中,我們常常都會使用到區域性變數,區域性變數一個特點就是當它超過作用域時,就會自動釋放。而autorelease pool跟區域性變數類似,當執行程式碼超過autorelease pool塊時,所有放在autorelease pool的物件都會自動呼叫release。它的工作原理如下:
建立一個NSAutoreleasePool物件
在autorelease pool塊的物件呼叫autorelease方法
釋放NSAutoreleasePool物件
引用Pro Multithreading and Memory Management for iOS and OS X的圖
iOS 5/OS X Lion前的(等下會介紹引入ARC的寫法)例項程式碼如下:
1 2 3 4 5 6 7 8 9 |
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// put object into pool
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];
/* 超過autorelease pool作用域範圍時,obj會自動呼叫release方法 */
|
由於放在autorelease pool的物件並不會馬上釋放,如果有大量圖片資料放在這裡的話,將會導致記憶體不足。
1 2 3 4 5 6 7 8 |
for
(int i = 0; i < numberOfImages; i++)
{
/* 處理圖片,例如載入
* 太多autoreleased objects存在
* 由於NSAutoreleasePool物件沒有被釋放
* 在某個時刻,會導致記憶體不足
*/
}
|
ARC管理方法
iOS/OS X記憶體管理方法有兩種:手動引用計數(Manual Reference Counting)和自動引用計數(Automatic Reference Counting)。從OS X Lion和iOS 5開始,不再需要程式設計師手動呼叫retain和release方法來管理Objective-C物件的記憶體,而是引入一種新的記憶體管理機制Automatic Reference Counting(ARC),簡單來說,它讓編譯器來代替程式設計師來自動加入retain和release方法來持有和放棄物件的所有權。
在ARC記憶體管理機制中,id和其他物件型別變數必須是以下四個ownership qualifiers其中一個來修飾:
__strong(預設,如果不指定其他,編譯器就預設加入)
__weak
__unsafe_unretained
__autoreleasing
所以在管理Objective-C物件記憶體的時候,你必須選擇其中一個,下面會用一些列子來逐個解釋它們的含義以及如何選擇它們。
__strong ownership qualifier
如果我想建立一個字串,使用完之後將它釋放呼叫,使用MRC管理記憶體的寫法應該是這樣:
1 2 3 4 5 |
{
//@"Hello, world"物件的RC=1
NSString *text = [[NSString alloc] initWithFormat:@
"Hello, world"
];
NSLog(@
"%@"
, text);
[text release];
//@"Hello, world"物件的RC=0
}
|
而如果是使用ARC方式的話,就text物件無需呼叫release方法,而是當text變數超過作用域時,編譯器來自動加入[text release]方法來釋放記憶體
1 2 3 4 5 6 7 |
{
//@"Hello, world"物件的RC=1
NSString *text = [[NSString alloc] initWithFormat:@
"Hello, world"
];
NSLog(@
"%@"
, text);
}
/*
* 當text超過作用域時,@"Hello, world"物件會自動釋放,RC=0
*/
|
而當你將text賦值給其他變數anotherText時,MRC需要retain一下來持有所有權,當text和anotherText使用完之後,各個呼叫release方法來釋放。
1 2 3 4 5 6 7 8 9 10 11 |
{
//@"Hello, world"物件的RC=1
NSString *text = [[NSString alloc] initWithFormat:@
"Hello, world"
];
NSLog(@
"%@"
, text);
|