iphone內(nèi)存管理詳解
在iPhone開發(fā)中,對內(nèi)存進行正確的管理是非常重要的一個方面。iPhone有128MRAM,但其中約有一半的容量要用于屏幕緩沖和其他系統(tǒng)進程,同時iPhone不支持將內(nèi)存寫到交換文件,所以iPhone只有大約64M的內(nèi)存用來運行應(yīng)用程序,且嚴格受到物理內(nèi)存量的限制。這樣,基本上不容許我們開發(fā)的軟件存在任何的內(nèi)存泄露。
由于iPhone對內(nèi)存嚴格的要求,所以當一個對象不再需要時,要及時釋放它所占用的內(nèi)存空間。
Objective-C 的內(nèi)存管理采用了基于引用計數(shù)(Reference Count)這種非常常用的技術(shù)。簡單講,每個對象都有一個與之關(guān)聯(lián)的整數(shù),可以將它稱為引用計數(shù)器或保留計數(shù)器,如果要使用一個對象,并確保在使用期間對象不被釋放,需要通過函數(shù)調(diào)用來取得“所有權(quán)”,即引用計數(shù)器加1,使用結(jié)束后再調(diào)用函數(shù)釋放“所有權(quán)”,使引用計數(shù)器減1。“所有權(quán)”的獲得和釋放,對應(yīng)引用計數(shù)的增加和減少。引用計數(shù)為正數(shù)時代表對象還有引用,為0時代表可以釋放。
copy 和 retain 的區(qū)別
copy: 建立一個索引計數(shù)為1的對象,然后釋放舊對象
retain:釋放舊的對象,將舊對象的值賦予輸入對象,再提高輸入對象的索引計數(shù)為1。
那上邊是什么意思呢?
Copy其實是建立了一個相同的對象,而retain不是:
比如一個NSString對象,地址為0×1111,內(nèi)容為@”STR”
Copy到另外一個NSString之后,地址為0×2222,內(nèi)容相同,新的對象retain為1,舊有對象沒有變化
retain到另外一個NSString之后,地址相同(建立一個指針,指針拷貝),內(nèi)容當然相同,這個對象的retain值+1
也就是說,retain是指針拷貝,copy是內(nèi)容拷貝。哇,比想象的簡單多了…
誤釋放對象
問題一:
- value = [array objectAtIndex:n]; //得到一個數(shù)組中的對象
- [arry removeObjectAtIndex:n]; //卸載那個對象
因為value得到了那個對象,但是由于另外一個擁有者release了該對象,所以其實value現(xiàn)在成了搖擺指針(無效數(shù)據(jù))
問題二:
- myArray = [NSArray array];
- ....
- [myArray release];
NSArray返回的是一個自動釋放對象,不僅myArray不應(yīng)該在一段時間后release,而應(yīng)該在適當?shù)臅r候先retain,以防止該array被系統(tǒng)誤釋放。
問題三:
- rocket = [rocketLauncher aRocket];
- [rocketLauncher release];
和array這種數(shù)據(jù)收集類對象一樣,如果我們得到了一個類的子對象而不retain它,那么在原父類被釋放的時候,這個rocket其實也會失去其意義。
Cocoa不同內(nèi)存管理環(huán)境下的autorelease
H 混合內(nèi)存管理環(huán)境:垃圾收集法(Garbage Collection)+索引計數(shù)法(Reference Counting)
雖然大多數(shù)情況下混合環(huán)境是不被推薦的,但是如果在這個情況下,autorelease需要注意以下事項:
垃圾收集混合環(huán)境下:應(yīng)該使用drain方法,因為release在GC模式下沒有意義
索引計數(shù)環(huán)境下:drain和release對于autoreleasepool(自動釋放池)的效果相同
對autorelease的誤解
A Cocoa的內(nèi)存管理分為 索引計數(shù)法(Reference Counting/ Retain Count)和 垃圾收集法(Garbage Collection)。而iPhone上目前只支持前者,所以autorelease就成為很多人的“捷徑”。
但是!autorelease其實并不是“自動釋放”,不像垃圾收集法,對對象之間的關(guān)系偵測后發(fā)現(xiàn)垃圾-刪除。但是autorelease其實是“延后釋放”,在一個運行周期后被標記為autorelease會被釋放掉。
切記小心使用autorelease,理解autorelease,防止在你還需要該對象的時候已經(jīng)被系統(tǒng)釋放掉了。
Interface Builder參與的內(nèi)存管理問題
要點:
如果一個變量在類中被定義為了 IBOutlet 那么你無需對其進行實例化,xib載入器會對其初始化。
如果一個變量在類中被定義為了 IBOutlet 那么你必須負責將其釋放。xib載入器不會幫忙的… …
*切不要初始化兩回,內(nèi)存會溢出,而且對象鎖定也會出錯。
關(guān)于索引計數(shù)(Reference Counting)的問題
*retain值 = 索引計數(shù)(Reference Counting)
NSArray對象會retain(retain值加一)任何數(shù)組中的對象。當NSArray被卸載(dealloc)的時候,所有數(shù)組中的對象會被執(zhí)行一次釋放(retain值減一)。不僅僅是NSArray,任何收集類(Collection Classes)都執(zhí)行類似操作。例如NSDictionary,甚至UINavigationController。
Alloc/init建立的對象,索引計數(shù)為1。無需將其再次retain。
[NSArray array]和[NSDate date]等“方法”建立一個索引計數(shù)為1的對象,但是也是一個自動釋放對象。所以是本地臨時對象,那么無所謂了。如果是打算在全Class中使用的變量(iVar),則必須retain它。
缺省的類方法返回值都被執(zhí)行了“自動釋放”方法。(*如上中的NSArray)
在類中的卸載方法“dealloc”中,release所有未被平衡的NS對象。(*所有未被autorelease,而retain值為1的)
NSString的內(nèi)存管理
如下實例:
- aString = @"I am a string that 2 years old, man!";
這種情況下,字符串儲存和管理由系統(tǒng)做,我們不用操心。
- aString = [NSString stringWithFormat:@"I am a string that %d years old, man!",2];
第二種情況下,我們需要去retain和release這個字符串,系統(tǒng)不管。
Objective-C內(nèi)存管理
1,你初始化(alloc/init)的對象,你需要釋放(release)它。例如:
- NSMutableArray aArray = [[NSArray alloc] init];
后,需要
- [aArray release];
2,你retain或copy的,你需要釋放它。例如:
- [aArray retain]
后,需要
- [aArray release];
3,被傳遞(assign)的對象,你需要斟酌的retain和release。例如:
- obj2 = [[obj1 someMethod] autorelease];
對象2接收對象1的一個自動釋放的值,或傳遞一個基本數(shù)據(jù)類型(NSInteger,NSString)時: 你或希望將對象2進行retain,以防止它在被使用之前就被自動釋放掉。但是在retain后,一定要在適當?shù)臅r候進行釋放。
為什么不能直接調(diào)用dealloc而是release
dealloc不等于C中的free,dealloc并不將內(nèi)存釋放,也不會將索引計數(shù)(Reference counting)降低。于是直接調(diào)用dealloc反而無法釋放內(nèi)存。
在Objective-C中,索引計數(shù)是起決定性作用的。
【編輯推薦】
iOS開發(fā):Objective-C優(yōu)雅的語法