一、關(guān)于內(nèi)存管理
應(yīng)用程序內(nèi)存管理是在程序運行時分配、使用以及在使用完成后釋放的過程。一個寫得好的程序使用盡可能少的內(nèi)存。
雖然內(nèi)存管理通常是在單個對象的級別上考慮的,但您的目標實際上是管理對象圖。

兩種內(nèi)存管理的方式:
MRC:可以通過跟蹤自己擁有的對象來顯式管理內(nèi)存。使用引用計數(shù)的模型來實現(xiàn)。
ARC:系統(tǒng)使用與MRC相同的引用計數(shù)系統(tǒng),但它在編譯時插入了適當(dāng)?shù)膬?nèi)存管理方法。(建議使用)
兩個內(nèi)存方面的常見問題:
釋放或覆蓋仍在使用的數(shù)據(jù)
這會導(dǎo)致內(nèi)存損壞,通常會導(dǎo)致應(yīng)用程序崩潰,或者更糟糕的是用戶數(shù)據(jù)損壞。
不釋放不再使用的數(shù)據(jù)會導(dǎo)致內(nèi)存泄漏
內(nèi)存泄漏是分配的內(nèi)存不被釋放的地方,即使它再也不會被使用。泄漏導(dǎo)致您的應(yīng)用程序使用越來越多的內(nèi)存,這反過來可能導(dǎo)致系統(tǒng)性能低下或您的應(yīng)用程序被終止。
二、內(nèi)存管理的基本規(guī)則
1、你擁有你創(chuàng)建的對象:
使用一個以“alloc”、“new”、“copy”或“mutableCopy”(例如,alloc、newObject或mutableCopy)開頭的方法創(chuàng)建一個對象。
2、您可以使用retain(strong)獲取對象的所有權(quán):
通常保證接收到的對象在接收到的方法中保持有效,并且該方法還可以安全地將對象返回到其調(diào)用者。
3、當(dāng)你不再需要它時,你必須放棄所有權(quán):
通過發(fā)送release消息或autorelease消息來放棄對對象的所有權(quán)。
4、你不能放棄對你不擁有的物品的所有權(quán)
注意:
1、使用autoRelease延遲釋放
2、你不擁有被引用返回的對象
3、當(dāng)應(yīng)用程序終止時,對象可能不會被發(fā)送dealloc消息。
由于進程的內(nèi)存在退出時會自動清除,所以簡單地允許操作系統(tǒng)清理資源比調(diào)用所有內(nèi)存管理方法更有效。
4、CoreFoundation使用類似當(dāng)不相同的規(guī)則
三、實用的內(nèi)存管理
1、使用訪問器方法使內(nèi)存管理更容易:
使用方法構(gòu)造器對實例變量賦值;
不要再init和dealloc方法使用方法構(gòu)造器
2、使用weak避免引用循環(huán)
3、避免使用中的對象被釋放
4、不要使用dealloc來管理稀缺資源
如果不這樣的話,會導(dǎo)致以下問題
1、tear-down機制本質(zhì)上是無序的,如果對象被意外釋放執(zhí)行dealloc
例如,tear-down順序可能會改變,這可能會導(dǎo)致意外的結(jié)果。
2、稀缺資源沒有被釋放
內(nèi)存泄漏是應(yīng)該修復(fù)的錯誤,但它們通常不會立即致命。
然而,如果當(dāng)你期望稀缺資源被釋放時,稀缺資源沒有被釋放,你可能會遇到更嚴重的問題。
例如,如果應(yīng)用程序耗盡了文件描述符,用戶可能無法保存數(shù)據(jù)。
3、清理邏輯在錯誤的線程上執(zhí)行。
如果一個對象是在一個意外的時間自動釋放的,它將被分配在任何線程的自動釋放池塊上。
如果它恰好是在對于只應(yīng)從一個線程中觸及的資源來說,這很容易是致命的。
5、集合擁有它們所包含的對象(array,dic,set)
添加到集合中時,對象的引用計數(shù)+1。
從集合中移除,或者集合本身被釋放時,對象的引用計數(shù)-1。
四、使用AutoreleasePool
1、關(guān)于autoreleasePool
autoreleasePool提供了一種機制,你可以丟掉對一個對象的所有權(quán),但是不會導(dǎo)致對象立即被釋放掉。
使用方式:
@autoreleasepool {
// Code that creates autoreleased objects.
}
在autoreleasepool結(jié)束的時候,在block塊里面被標記為autorelease的對象會收到一次release消息。
autoreleasepool可以嵌套使用。
AppKit和UIKit在每次runloop時,都會放到autoreleasepool的block中使用。所以一般來說不用顯性的手動創(chuàng)建。
除非以下幾種情況:
1、如果你沒有基于UI framework寫程序,如一個command-line工具
2、你寫了一個循環(huán),創(chuàng)建了很多的臨時對象
此時你需要創(chuàng)建一個autoreleasePool,在每次循環(huán)結(jié)束的時候釋放掉臨時變量
3、你創(chuàng)建了一個輔助線程(貌似只有舊版本會有問題)
2、使用本地自動釋放池塊減少峰值內(nèi)存足跡
例子:
NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
@autoreleasepool {
NSError *error;
NSString *fileContents = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding error:&error];
/* Process the string, creating and autoreleasing more objects. */
}
}
三、Autorelease Pool Block和線程
Cocoa應(yīng)用程序中的每個線程都維護自己的Autorelease Pool Block堆棧。如果您正在編寫一個只有Foundation-only的程序,或者如果分離一個線程(沒有使用GCD或者NSThread),則需要創(chuàng)建自己的Autorelease Pool Block。
如果您的應(yīng)用程序或線程壽命很長,并且可能生成大量自動釋放的對象,則應(yīng)該使用自動釋放池塊(如AppKit和UIKit在主線程上做);否則,自動釋放的對象會積累,內(nèi)存占用空間也會增加。如果分離的線程不進行Cocoa調(diào)用,則不需要使用自動釋放池塊。
四、NSCopying
NSCOpying協(xié)議聲明了一種提供對象功能副本的方法。
“副本”的確切含義可能因類而異,但副本必須是一個功能獨立的對象,其值與復(fù)制時的原始值相同。
用NSCOpying制作的副本由發(fā)送者隱式保留,發(fā)送者負責(zé)釋放它。
聲明一個方法,copyWithZone:,但是復(fù)制通常使用方便方法副本調(diào)用。復(fù)制方法是為所有NSOBjects定義的,并簡單地調(diào)用具有默認區(qū)域的copyWithZone:。