iOS内存管理策略和实践
來源:http://www.baidu.com/link?url=irojqCBbZKsY7b0L2EBPkuEkfJ9MQvUf8kuNWQUXkBLk5b22Jl5rjozKaJS3n78jCnSsUZjVQvFIW1IKcJMlR2fGj9eiy-gCY7ulTRyEuAi
關(guān)于iOS內(nèi)存管理 應(yīng)用程序內(nèi)存管理是:程序運行時,開辟的內(nèi)存空間。使用它,釋放它的過程,寫的好的程序盡可能少使用內(nèi)存。在Objective-C中,內(nèi)存管理被看做是:在很多數(shù)據(jù)、代碼下,分配
轉(zhuǎn)自hherima的博客 內(nèi)存管理策略(memory Management Policy) NSObject protocol中定義的的方法和標(biāo)準(zhǔn)命名慣例一起提供了一個引用計數(shù)環(huán)境,內(nèi)存管理的基本模式處于這個環(huán)境中。NSObject類定義了一個方法叫dealloc,當(dāng)對象銷毀的時候,dealloc會被自動調(diào)用。本文描述,在Cocoa中所有正確管理內(nèi)存基本規(guī)則,并提供了一些使用正確的例子。 【基本的內(nèi)存管理規(guī)則】 內(nèi)存管理模式基于對象的“所有權(quán)”上。任何對象都會被有一個或多個使用者引用,只要對象還有一個使用者,該對象就應(yīng)該繼續(xù)存在。如果一個對象沒有使用者了,系統(tǒng)將自動銷毀它。為了讓開發(fā)者清晰的了解:使用對象和不再使用對象的場景,Cocoa設(shè)置了以下策略: 1.管好自己創(chuàng)建的對象。開發(fā)者使用alloc、new、copy和mutableCopy來創(chuàng)建對象。 2.使用retain來獲得對象的所有權(quán)。某個函數(shù)接受的對象,通常保證在該函數(shù)調(diào)用期間仍然可用,并可以安全返回對象給上層調(diào)用者。開發(fā)者在以下兩種情況下使用retain 1)在“訪問函數(shù)”(accessor)的實現(xiàn)中或者在init方法,為了將對象作為自己的屬性。 2)防止對象被其他操作釋放掉,從而變?yōu)闊o效的對象。 3.當(dāng)你不在需要的時候,必須放棄對象所有權(quán)。 一個簡單的例子 看看下面的代碼段,可以證明剛剛的所說的策略?內(nèi)存管理實踐
盡管基本的概念在“內(nèi)存管理策略”文章中簡單得闡述了,但是還有一些實用的步驟讓你更容易管理內(nèi)存;有助于確保你的程序最大限度地減少資源需求的同時,保持可靠和強大。
?
使用“訪問器方法”讓內(nèi)存管理更簡單
假如,你的程序有一個對象類型的屬性,你必須保證:當(dāng)你使用的時候,任何的已經(jīng)賦值了的對象不會被銷毀。被賦新值的時候,開發(fā)者必須獲得對象的所有權(quán),并放棄正在使用對象的所有權(quán)。
?
有時候,這些聽起來很老套和繁瑣,如果開發(fā)者統(tǒng)一使用訪問器方法,內(nèi)存管理有問題的機會大大減少。如果開發(fā)者在代碼中總是使用retain和release管理實例變量,幾乎肯定會做錯事,換句話說:訪問器是必須的。
?
來看一個Counter對象
該屬性聲明了兩個訪問器。典型的做法,開發(fā)者告訴編譯器合成(synthesize)訪問器方法。了解訪問器是如何實現(xiàn)的對開發(fā)者是有好處的。
?
在get訪問器中,開發(fā)者只需要返回變量即可,不需要retain和release
?
在set訪問器方法里,如果每一位開發(fā)者都能按照相同的規(guī)則,對新的count進行負責(zé),開發(fā)者必須通過retain來獲得對象的所有權(quán)。開發(fā)者也需要通過release來放棄舊對象的所有權(quán)。(在Objective-c中給對象發(fā)送nil消息是允許的,即是_count沒有被設(shè)置,仍然是安全的。)為了防止兩個對象是相同的,開發(fā)者需要先調(diào)用[newCount retain](開發(fā)者可不希望意外的把對象給銷毀)
?
【使用訪問器方法設(shè)置屬性值】
假設(shè)你想實現(xiàn)一個重置counter的方法。你有多個選擇。
?
第一種方式:用alloc創(chuàng)建NSNumber實例,然后可以用release釋放。
第二種方法:用便捷構(gòu)造方法創(chuàng)建NSNumber對象。因此,不用調(diào)用retain和release等。
注意:以上兩種方法都使用訪問器方法。
?
下面將幾乎可以肯定,正常的情況下,因為它可能避開訪問器方法,這樣很誘人。這樣做幾乎肯定會導(dǎo)致一個錯誤在某些時候。(比如,當(dāng)開發(fā)者忘記retain或者release,或者將來內(nèi)存管理機制有變化)
?注意:如果開發(fā)者使用key-value observing,上面這種方法也不屬于KVO范疇
?
不要在初始化和dealloc函數(shù)中使用訪問器方法
唯一不讓使用訪問器的地方就是initializer和dealloc。為了將counter初始化為零,開發(fā)者可以這樣實現(xiàn):
?
為了初始化為非零數(shù)據(jù),可以這么實現(xiàn)initWithCount方法:
?
既然Counter類有一個類屬性,開發(fā)者還需要實現(xiàn)dealloc,dealloc通過發(fā)送release,將放棄左右對象的所有權(quán),最終dealloc還會調(diào)用父類的dealloc函數(shù)。
?
使用弱引用(Weak References)來避免循環(huán)retain
對一個對象retain操作是強引用(strong reference)。所有強引用都被release之后對象才被銷毀。如果,兩個對象有彼此的強引用,就出現(xiàn)眾所周知的問題——循環(huán)retain。(包括:直接,或通過其他對象強引用鏈的情況)
?
對象的關(guān)系如圖所示,有一個潛在的循環(huán)retain。Document對象每一頁都有一個Page對象。每個Page對象有一個paragraphs屬性,表明該Page在那一Document中。如果Document中的Page是強引用,Page類中的paragraphs屬性也是強引用,那個對象都不會被銷毀。Document的引用值直到Page對象被釋放才變?yōu)?,而Page對象直到Document釋放才被釋放。
解決循環(huán)引用的的方法是使用弱引用。弱引用是一種非占有所有權(quán)的關(guān)系,不對源對象retain,只是引用(reference)。然后,為了保持對象圖的完整性,強引用還是必要的(如果只有弱引用,pages和paragraphs將沒有任何的所有者,也就不能被釋放)Cocoa形成了一個慣例:父對象應(yīng)該強引用子對象,子變量應(yīng)該弱引用父對象。所以,在圖1中,Document對象強引用page對象,page對象弱引用Document對象。
?
Cocoa中的弱引用例子包含了(但不限于)table data sources, outline view items, notification observers, and miscellaneous targets and delegates.
?
開發(fā)者給弱引用對象發(fā)送消息應(yīng)小心一些。如果給一個已經(jīng)銷毀的對象發(fā)消息,程序?qū)rash。當(dāng)對象可用的時候,開發(fā)者需具備良好的定義條件(You must have well-defined conditions for when the object is valid.)。
?
大多數(shù)情況:弱引用對象知道其他對象弱引用了自己,當(dāng)自己被銷毀的時候,有責(zé)任通知其他對象。比如,當(dāng)開發(fā)者用notification center注冊一個對象,notification center存儲一個弱引用對象,并發(fā)送post消息給對象。當(dāng)對象已經(jīng)被銷毀了。
?
開發(fā)者需從notification center中注銷對象,防止notification center發(fā)送消息給不存在的對象。同樣,當(dāng)一個delegate對象被銷毀后,開發(fā)者噓移除delegate通過發(fā)送一個參數(shù)為nil的setDelegate消息而這些消息通常從對象的dealloc中發(fā)送。
?
避免銷毀正在使用的對象
Cocoa's所有權(quán)策略指定接受的對象應(yīng)該,保證在函數(shù)調(diào)用范圍可用;還可以返回接受的對象,而不用擔(dān)心被release掉。保證從程序中的gerrer方法中返回實例變量或者計算后的值是沒問題的。
?
問題是,當(dāng)需要的時候?qū)ο笕匀挥行АE紶栍行├?#xff0c;主要是下面兩種情況:
?
1.從基礎(chǔ)集合類中移除對象
當(dāng)對象從基本集合類移除,集合類發(fā)送一個release(而不是autorelease)消息。如果集合類是對象的唯一擁有者,被移除的對象(例子中heisenObject)就被立即銷毀。
?
2.父對象被銷毀
?某些情況,開發(fā)者從其他對象獲得一個對象,直接或見解釋放父對象。release父對象導(dǎo)致被銷毀,父對象又是子對象的唯一擁有者,子對象將同時被銷毀。
?
防止這些情況,當(dāng)開發(fā)者收到heisenObject時retain,使用完release掉,比如:
?
不要對稀缺資源進行dealloc
不要在dealloc函數(shù)中管理file descriptor、network connections、buffer和caches這些資源。通常,開發(fā)者不應(yīng)設(shè)計帶有dealloc這樣的類。dealloc可能延遲調(diào)用,要么就成為程序的一個bug或者造成程序崩潰。
?
相反,如果你有一個稀缺資源的類,你應(yīng)該這樣設(shè)計應(yīng)用程序,例如,你知道當(dāng)你不再需要的資源的時候,然后可以告訴實例clean up。通常,你會再釋放該實例,緊接著調(diào)用dealloc,你不會受到額外的問題。
?
如果您嘗試在dealloc中背馱式得資源管理,可能會出現(xiàn)問題。
1.順序依賴被拆散。雖然開發(fā)者可能希望得到一個特定順序,被拆散的對象圖本質(zhì)上是無序的。如果對象是被autorelease的,被拆散的順序可能還有變化,也可能導(dǎo)致意想不到的結(jié)果。
2.非回收式的稀缺資源。內(nèi)存泄露是可以修復(fù)bug,內(nèi)存泄露的傷害不是立即致命的。如果稀缺資源在不能釋放的時候,被你釋放了,你可能會碰到更嚴(yán)重的問題。如果你的應(yīng)用程序使用文件描述符(file descriptor),可能導(dǎo)致不能寫數(shù)據(jù)。
3.在錯誤的線程中執(zhí)行cleanup邏輯。如果一個對象被開發(fā)者設(shè)置為是autorelease的,它會被任意一個“它正好存在于的”線程的自動釋放池給釋放掉。這是很容易的致命錯誤:該資源應(yīng)該在一個線程中使用和釋放。
(If an object is autoreleased at an unexpected time, it will be deallocated on whatever thread’s autorelease pool block it happens to be in. This can easily be fatal for resources that should only be touched from one thread)
?
集合擁有它所包含的對象
當(dāng)你添加一個對象到集合(如,array,dictionary和set),集合獲得對象的所有權(quán)。對象被移除的時候或者集合本身release的時候,放棄對象的所有權(quán)。如果開發(fā)者想創(chuàng)建一個帶有粒度的array,可以這么搞:
?
這種情況,開發(fā)者沒有調(diào)用alloc,所以無需掉用release。也沒有必要retain新的convenienceNumber。
這個情況,開發(fā)者需要向allocedNumber發(fā)送一個release消息在for的作用域之內(nèi),來匹配alloc。既然array的addObject方法 retain了allocedNumber,allocedNumber就會被array管理刪除。
?
要像理解這些,把自己當(dāng)做設(shè)計集合類的開發(fā)者們。你想保證:即使不在你照看下,集合中的變量能仍然好用,所以,當(dāng)對象傳入的時候,你retain了一次。當(dāng)對象移除集合類,你需要發(fā)送release消息。
?
對象所有權(quán)策略是基于引用計數(shù)實現(xiàn)的
對象所有權(quán)的策略是通過引用計數(shù)——通常叫做retain count實現(xiàn)的。每一個對象有一個retaincount變量。
1.創(chuàng)建對象后,它的retaincount是1
2. retain之后,retain count +1
3.release之后 retain count -1?
4.autorelease之后,在自動釋放池最后-1
5.對象的retain count減少到0的時候,對象被銷毀。
?
【重要】
不要顯式調(diào)用對象的retainCount,結(jié)果往往具有誤導(dǎo)性,作為開發(fā)者可能不了解框架式如何對對象retain的。在調(diào)試內(nèi)存管理中,你應(yīng)該只關(guān)注確保你的代碼遵循所有權(quán)規(guī)則。
?
原文:Practical Memory Management
關(guān)于iOS內(nèi)存管理
應(yīng)用程序內(nèi)存管理是:“程序運行時,開辟的內(nèi)存空間。使用它,釋放它”的過程,寫的好的程序盡可能少使用內(nèi)存。在Objective-C中,內(nèi)存管理被看做是:“在很多數(shù)據(jù)、代碼下,分配受限內(nèi)存資源所有權(quán)方法”。當(dāng)你依據(jù)這個指南完成你的程序時,你將獲得“通過顯式管理對象的命周期,不使用的時候釋放他們,來管理程序內(nèi)存”的知識。
?
盡管,典型的內(nèi)存管理是作用于單個對象,你的目標(biāo)是通過管理對象圖。你想確保:在內(nèi)存中沒有比實際需要的還多的對象。
概述
Objective-C提供兩種內(nèi)存管理的方法:
1.“manual-release”(MRR),需要顯式管理內(nèi)存通過跟蹤對象的所有權(quán)。MRR基于NSObject類在運行時提供的引用計數(shù)實現(xiàn)的。
2.“Automatic Reference Counting”ARC,系統(tǒng)使用相同的引用計數(shù)基于MRR,但是在編譯時,為開發(fā)者適當(dāng)插入一些內(nèi)存管理方法。強烈建議開發(fā)者在新項目中使用ARC。使用ARC就無需理解本文所描述的內(nèi)容了。
?
防止內(nèi)存相關(guān)問題的好的做法
兩個主要的內(nèi)存管理誤用問題
1.釋放或覆蓋正在使用的數(shù)據(jù)。這將造成內(nèi)存損壞,造成應(yīng)用程序崩潰,或者更壞的情況是損壞用戶數(shù)據(jù)。
2.沒有釋放數(shù)據(jù),導(dǎo)致內(nèi)存泄露。泄漏導(dǎo)致應(yīng)用程序的內(nèi)存使用量逐漸增加,這反過來又可能會導(dǎo)致系統(tǒng)性能較差或者應(yīng)用程序被終止(crash)
?
引用計數(shù)內(nèi)存管理的角度思考,但是,往往是適得其反,因為你往往會考慮內(nèi)存管理方面的實現(xiàn)細則,而不是在你的實際目標(biāo)。相反,你應(yīng)該想到的內(nèi)存管理對象所有權(quán)和對象圖的角度。
1.Cocoa使用簡單的命名慣例來指示,是否擁有函數(shù)返回的對象。(點擊查看內(nèi)存管理策略)
2.盡管內(nèi)存管理基本策略很簡單,有一些實際的步驟,你可以使內(nèi)存管理更輕松,有助于確保你的程序仍然可靠和穩(wěn)定的,而在同一時間最大限度地減少資源需求。(點擊查看內(nèi)存管理實踐)
3.Autorelease pool 提供一種機制:讓對象延遲release。這個對你想放棄所有權(quán),但又想避免立即釋放(比如函數(shù)的返回值)。有些時候,你可能會使用自己的autorelease池塊。(點擊查看自動釋放池)。
?
使用分析工具來調(diào)試內(nèi)存問題
在編譯時候找出代碼的問題。使用Xcode內(nèi)嵌的Clang Static Analyzer 。
如果內(nèi)存管理的問題仍然發(fā)生,還有其他的工具和技術(shù),你可以用它來識別和診斷問題。
1.多數(shù)工具和技術(shù)都在TN2239中有描述,iOS Debugging Magic 特別是NSZombie來幫助找到過度釋放對象。
2.使用Instruments來追蹤引用計數(shù)事件并找到內(nèi)存泄露。( 參考?Collecting Data on Your App)
?
原文:About Memory Management
轉(zhuǎn)載于:https://www.cnblogs.com/sunminmin/p/3862661.html
總結(jié)
以上是生活随笔為你收集整理的iOS内存管理策略和实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [Linux][Hadoop] 将had
- 下一篇: hdu 3237