原文地址:http://flandycheng.blog.51cto.com/855176/559397
開發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 . 就是在需要的時候再從硬碟上讀取,而避免常駐記憶體。