Objective-C的內(nèi)存管理實質(zhì)上就是引用計數(shù)。
從前是手動引用計數(shù)(MRC),現(xiàn)在是自動引用計數(shù)(ARC)。
所謂ARC,就是讓編譯器來進(jìn)行內(nèi)存管理,現(xiàn)在Xcode默認(rèn)ARC為有效狀態(tài)。
引用計數(shù):
Objective中,生成對象時引用計數(shù)為1,持有對象引用計數(shù)加一,釋放對象引用計數(shù)減一,當(dāng)對象引用計數(shù)為0時則釋放對象。
在ARC下我們不必關(guān)心引用計數(shù)這個概念。但是在MRC下必須清晰引用計數(shù),而且要牢記:自己生成的對象自己持有,非自己生成的對象自己也能持有,不再需要自己持有的對象要釋放,非自己持有的對象不能釋放。下面詳細(xì)說明:
MRC下:
拿數(shù)組來說,可以使用這兩種方式創(chuàng)建:一種是使用alloc方法,另一種是使用array等一系列方法。那這兩種方法有什么區(qū)別:
使用alloc方法生成的對象自己持有(持有的概念通過下面的對比會清楚),當(dāng)不再需要時自己釋放(調(diào)用release方法),當(dāng)一個對象的引用計數(shù)為0時不可以再讓變量調(diào)用release(非自己持有的對象不能釋放)。
使用array方法只是取得對象的存在,就是說你可以通過變量訪問這個對象,但是你不持有它,當(dāng)你不再需要使用這個對象時你不必調(diào)用release方法。你可以對變量調(diào)用retain方法來持有對象,當(dāng)你不需要使用對象時調(diào)用release方法釋放對象。
為什么通過array方法獲得的對象有這個特性?
首先說明這不是ARC。這就要介紹autorelease了。
我們使用release方法會立即釋放一個對象,而使用autorelease方法會將對象注冊到autoreleasepool中,具體的步驟如下:
生成NSAutoreleasePool對象,對不需要被持有的對象調(diào)用autorelease方法,廢棄NSAutoreleasePool對象。當(dāng)NSAutoreleasePool對象被廢棄時,調(diào)用過autorelease方法的對象都會被釋放。在iOS開發(fā)中,主線程的NSRunLoop負(fù)責(zé)對自動釋放池生成廢棄,這里不再贅述,當(dāng)然我們可以自己創(chuàng)建自動釋放池,及時廢棄大量占用內(nèi)存的對象。需要注意一點:無論哪種類型的變量調(diào)用autorelease方法、都是調(diào)用NSObject類的autorelease方法,但是NSAutoreleasePool類的autorelease方法已被該類重載,如果NSAutoreleasePool的對象調(diào)用autorelease方法時會出現(xiàn)運行時錯誤。
另外使用以下方法、或以以下方法開頭時意味著自己生成的對象自己持有:alloc、new、copy、mutablecopy。
ARC:
在MRC下,我們通過retain、release、autorelease來完成引用計數(shù)(內(nèi)存管理),在ARC下如何記述如何管理呢?
所有權(quán)限修飾符:
__strong:id和對象類型默認(rèn)的所有權(quán)限修飾符。使用__strong修飾的變量在超出作用域時(或者手動置nil、被賦值等),對象被廢棄。即隨著強引用的失效,引用的對象隨之釋放。
但是__strong還不夠“strong”,使用__strong時會出現(xiàn)相互強引用的情況:
相互強引用是內(nèi)存泄漏的一種情況,對象得不到應(yīng)有的釋放。這里詳細(xì)說一下相互強引用:
首先要理解這個過程:比如說有個類A,它的一個實例a,類A中有一個類B的成員變量bb,a和a的成員變量bb都被初始化了,當(dāng)a被廢棄時會怎么樣? ? a超出作用域時,a之前所引用的對象被釋放,然后變量bb被廢棄,bb之前所引用的對象被釋放。再來說這種情況,有個類A,它的一個實例a,類A中有一個類B的成員變量bb;有個類B,它的一個實例b,類B中有一個類A的成員變量aa,a和b初始化之后,分別將a、b賦給aa、bb。當(dāng)a和b超出作用域之后,a所引用的對象沒有被釋放,因為aa還在引用它,b所引用的對象沒有被釋放,因為bb還在引用它。
__weak提供弱引用,__weak不持有對象,也就是說對象失去強引用之后無論有多少弱引用都將被釋放。弱引用還會自動失效處于nil狀態(tài)。
__unsafe_unretained是不安全的所有權(quán)修飾符,使用該修飾符修飾的變量不屬于編譯器的內(nèi)存管理對象,當(dāng)其引用的對象被釋放后不會自動置nil。C語言的結(jié)構(gòu)體中,不能含有OC對象,因為C語言沒有方法來管理結(jié)構(gòu)體成員的生存周期,要想加入OC對可以強制轉(zhuǎn)換為void *或使用__unsafe_unretained修飾符。
__autoreleasing修飾符:
這個修飾符是與MRC下的autorelease方法對應(yīng)的修飾符。在ARC下不能使用autorelease方法,也不能使用NSAutoreleasePool類,但是可以使用@autoreleasepool { ?} 塊來替代NSAutoreleasePool的作用、使用__autoreleasing修飾符代替調(diào)用autorelease方法。
以下幾種情況非顯示的使用__autoreleaseing:
不是 ?以alloc、new、copy、mutableCopy開頭的方法 的返回值的對象 ?默認(rèn)注冊到autoreleasePool中。
在訪問__weak修飾的變量時,該變量默認(rèn)被注冊到autoreleasePool中,因為__weak只持有對象的弱引用,在訪問的過程中很可能對象被廢棄了。這樣可以保證在@autoreleasePool{}塊結(jié)束前對象都不會被釋放。
init方法返回的對象不注冊到autoreleasePool。(以init開頭的方法必須是實例方法,并且必須返回id類型或者該方法所在類(也可以是其父類或子類)類型的對象。(-(void)initialiaze方法除外)。
要注意只有相同所有權(quán)修飾符相同的變量才可以賦值,否則編譯報錯,如果沒有報錯則說明編譯器自行幫你轉(zhuǎn)化了。
最后給出屬性與所有權(quán)限修飾符的關(guān)系:
assign__unsafe_unretained
copy__strong
retain__strong
strong__strong
unsafe_unretained ? __unsafe_unretained
weak__weak