iOS內(nèi)存管理

官方文檔提要:https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html#//apple_ref/doc/uid/10000011i

一、關(guān)于內(nèi)存管理

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

iOS內(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:。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容