開發iPhone 應用程序並不難,基本上就是三個詞 - 「memory, memory, memory」 。iPhone OS 對內存的要求很嚴格,有memory leak ,殺掉; 內存使用超限額,殺掉。一個通過測試的程序,在使用過程當中90%以上的崩潰都是內存問題形成的。在這裏簡單總結一下Object-C 內存管理。
基本概念
Object-C 的內存管理基於引用計數(Reference Count)這種很是經常使用的技術。簡單講,若是要使用一個對象,並但願確保在使用期間對象不被釋放,須要經過函數調用來取得「全部權」,使用結束後再調用函數釋放「全部權」。「全部權」的得到和釋放,對應引用計數的增長和減小,爲正數時表明對象還有引用,爲零時表明能夠釋放。
函數
得到全部權的函數包括
alloc - 建立對象是調用alloc,爲對象分配內存,對象引用計數加一。
copy - 拷貝一個對象,返回新對象,引用計數加一。
retain - 引用計數加一,得到對象的全部權。
另外,名字中帶有alloc, copy, retain 字串的函數也都認爲會爲引用計數加一。
釋放全部權的函數包括
release - 引用計數減一,釋放全部權。若是引用計數減到零,對象會被釋放。
autorelease - 在將來某個時機釋放。下面具體解釋。
autorelease
在某些狀況下,並不想取得全部權,又不但願對象被釋放。例如在一個函數中生成了一個新對象並返回,函數自己並不但願取得全部權,由於取得後再沒有機會釋放(除非創造出新的調用規則,而調用規則是一切混亂的開始),又不可能在函數內釋放,能夠藉助autorelease 。所謂autorelease , 能夠理解爲把全部權交給一個外在的系統(這個系統實際上叫autorelease pool),由它來管理該對象的釋放。一般認爲交給 autorelease 的對象在當前event loop 中都是有效的。也能夠本身建立NSAutoreleasePool 來控制autorelease的過程。
據蘋果的人說,autorelease效率不高,因此能本身release的地方,儘可能本身release,不要隨便交給autorelease來處理。
規則
引用計數系統有本身的引用規則,遵照規則就能夠少出錯:
得到全部權的函數要和釋放全部權的函數一一對應。
保證只有帶alloc, copy, retain 字串的函數纔會讓調用者得到全部權,也就是引用計數加一。
在對象的 dealloc函數中釋放對象所擁有的實例變量。
iPhone 開發過程當中,內存的使用相當重要。不但要合理分配使用內存,還要注意內存泄露的問題, 由於內存泄露會致使程序因爲內存不足而崩潰。根據我的開發的經驗來看,在開發iPhone程序的過程當中,關於內存的問題須要注意如下幾點:
內存分配、釋放成對出現
使用 alloc 分配的內存對象須要在用完後 調用release釋放
注意copy,retain,assign操做符的區別
copy, retain操做符賦值的對象和alloc同樣,須要release釋放,不然會致使內存泄露
assign 操做符的含義是將對象指向另外一對象, 二者指向的是同一內存對象,無需調用release釋放
NSArray, NSDictionary, NSMutableArray, NSMutableDictionary等容器類, 在使用這些容器類的時候要注意, 在添加對象到這些類對象時,容器類會自動調用一次retain,好比
NSString* string = [[NSString alloc] initWithString:@」test string」]; // refCount = 1
NSArray* array = [NSArray array];
[array addObject:string]; // refCount = 2
[string release]; // refCount = 1
這種狀況, 即使string已經調用release,可是在加入 array中時已經調用了一次retain,注意refCount的變化 簡單介紹一下iPhone 或者說Objective C對對象的管理機制。 OC中採用一種引用計數refCount的方式來管理內存對象,當refCount等於0的時候就會釋放對象所佔的內存, 操做符alloc,copy, retain都會將refCount加1表示引用計數增長, 而調用release使 refCount自動減1, 當refCount=0時表示該對象已經沒有被引用,能夠將其釋放, 以後該對象便不可用
連續重複分配內存的過程最好建立本身的自動釋放池 NSAutoreleasePool,一般是在for、while等循環操做過程當中,好比
for( int i=0; i < 100; i++ )
{
NSString* str = [[NSString alloc] initWithString:@」some string」];
// 針對str的操做
[str release];
}
在這種狀況下,有2點須要注意,首先若是可能,就把str的分配、釋放放在for循環外面, 從而減小內存的分配、釋放致使程序效率低下,也利於內存回收,如上例應該爲
NSString* str = [[NSString alloc] initWithString:@」some string」];
for( int i=0; i < 100; i++ )
{
// 針對str的操做
}
[str release];
若是實際狀況複雜,不能像例子中那樣抽離出循環外,須要建立本身的內存管理池, 一樣適用於須要大量autorelease對象的過程
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
for(int i=0; i < 100; i++ )
{
// actions
}
[pool release];
之因此要這樣作,是由於apple處理iPhone的內存管理機制問題, 一般狀況下,系統會在須要的時候釋放整理全部的autorelease對象,這就是爲何有時候autorelease對象在做用域範圍外還有多是有效的
避免不經常使用對象駐留內存, 桌面開發的tx不少喜歡在程序初始化的時候將某些資源好比小圖片加載進內存,從而提升程序運行效率。 但這種方式在iPhone以及其它mobile移動設備開發時須要避免,由於對於這些設備來講,內存永遠顯得不足(固然普通pc內存也是越大越好:) )。 按照apple的官方說法, Load resources lazily . 就是在須要的時候再從硬盤上讀取,而避免常駐內存。