貌似每個(gè)iOS開發(fā)者都有一篇屬于自己的內(nèi)存管理概龄,記錄了自己對(duì)內(nèi)存管理理解的深度以及廣度淆两,所以我也來(lái)記錄一下我的理解鬼店。
MRC
1.與引用計(jì)數(shù)相關(guān)的alloc/copy/mutableCopy/retain/realease/deallloc
- 相關(guān)操作
對(duì)象操作 | Objective-C方法 | 引用計(jì)數(shù) |
---|---|---|
生成并持有對(duì)象 | alloc/new/copy/mutablecopy | 1 |
持有對(duì)象 | retain方法 | +1 |
釋放對(duì)象 | release | -1 |
廢棄對(duì)象 | dealloc | 0 |
- 原則
-
自己生成的對(duì)象咐柜,自己持有
id object = [[NSObject alloc] init]; //以上代碼通過(guò)alloc生成了一個(gè)對(duì)象,并且object持有該對(duì)象通孽,rc = 1
另外序宦,使用new方法也能生成并持有對(duì)象,[NSObject new]和[[NSObject alloc] init]使用效果是完全一致的背苦。
-
自己生成的對(duì)象咐柜,自己持有
-
不是自己生成的對(duì)象挨厚,自己也能持有
通過(guò)使用表格第一行生成的對(duì)象堡僻,自己都能持有,對(duì)于alloc/new/copy/mutablecopy 以外的方法生成的對(duì)象疫剃,自己就不是持有者了钉疫,例如id obj = [NSMutableArray array];
,obj就不持有array返回的對(duì)象,僅僅取得對(duì)象的引用巢价,但可以通過(guò)[obj retain]
讓obj持有該對(duì)象牲阁。 -
誰(shuí)持有,誰(shuí)釋放壤躲,不持有城菊,不能釋放,不再需要時(shí)碉克,主動(dòng)釋放
以上兩句話很好理解凌唬,誰(shuí)通過(guò)表格第一行的方式生成并持有對(duì)象,或者通過(guò)retain的方式持有了對(duì)象漏麦,一旦不需要該對(duì)象了客税,一定要記得調(diào)用release
方法釋放該對(duì)象。但只有自己不持有該對(duì)象撕贞,就一定不能調(diào)用release
更耻。
2. MRC中的autorelease
autorelease故名思議就是自動(dòng)釋放,看上去很像ARC捏膨,但其實(shí)更類似于C語(yǔ)言中的局部變量秧均,也就是說(shuō),超出變量作用域的時(shí)候?qū)⒆詣?dòng)被廢棄号涯,但這里與C語(yǔ)言不同的是目胡,編程人員可以手動(dòng)設(shè)置其作用域。
autorelease的具體使用方法如下:
(1).生成并持有NSAutoreleasePool對(duì)象
(2).調(diào)用已分配的autorelease方法
(3).廢棄NSAutoreleasePool對(duì)象
? NSAutoreleasePool對(duì)象的生命周期相當(dāng)于C語(yǔ)言變量的作用域链快,對(duì)于所有調(diào)用過(guò)autorelease方法的對(duì)象誉己,在廢棄NSAutoreleasePool對(duì)象時(shí),都將對(duì)對(duì)象統(tǒng)一調(diào)用release方法久又,代碼如下所示:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init] ; id obj = [ [NSObject alloc]init]; [obj autorelease] ; [pool drain];
? ? 另外在Cocoa框架中,如果不是使用alloc/new/copy/mutablecopy這幾個(gè)方法返回的對(duì)象效五,其余方法返回的對(duì)象都將自動(dòng)注冊(cè)到NSAutoreleasePool中
id array = [NSMutableArray arrayWithCapacity:10];
其實(shí)也就等同于:
id array = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
3.實(shí)現(xiàn)(以上操作對(duì)應(yīng)于runtime下的實(shí)現(xiàn))
- retainCount/retain/release的實(shí)現(xiàn):
該理論總結(jié)自《Pro multithreading and memory management for iOS and OS X》
由于Apple對(duì)于NSObject類的源代碼沒有公開地消,此處是利用Xcode的調(diào)試器(lldb)和iOS大概追述其實(shí)現(xiàn)過(guò)程,在NSObject類的alloc類方法上設(shè)置斷點(diǎn)畏妖,追蹤程序的執(zhí)行脉执,所以大概得到的結(jié)論便是Apple采用散列表(引用計(jì)數(shù)表),來(lái)管理引用計(jì)數(shù),通過(guò)傳入對(duì)象的地址便可以返回對(duì)象的引用計(jì)數(shù)戒劫。
- autorelease的實(shí)現(xiàn)
通過(guò)查看GNUstep的源代碼可以知道半夷,當(dāng)對(duì)象調(diào)用autorelease方法的時(shí)候婆廊,實(shí)際上的本質(zhì)就是調(diào)用NSAutoreleasePool的addObject()方法。(所以pool當(dāng)然是一個(gè)數(shù)組類型的結(jié)構(gòu)嘍)
-(id)autorelease{ [NSAutoreleasePool addObject:self]; }
而當(dāng)pool調(diào)用drain方法的時(shí)候則是讓pool中的每個(gè)對(duì)象都調(diào)用release方法巫橄。
ARC
1.ARC中的變量所有權(quán)修飾符_ _ strong,_ _ weak, _ _ unself_unretained,_ _ autoreleasing,
ARC有效時(shí)淘邻,id類型和對(duì)象類型同C語(yǔ)言的其他類型不同,其類型上必須加上所有權(quán)修飾符湘换,不加時(shí)默認(rèn)為_ _strong.
- _ _strong
_ strong修飾符是id類型和對(duì)象類型的默認(rèn)所有權(quán)修飾符宾舅,也就是說(shuō),以下代碼中默認(rèn)是添加了修飾符的彩倚,并且是 _strong:
{ id obj = [[NSObject alloc]init]; }
實(shí)際上是:
{ id __strong obj = [[NSObject alloc] init]; }
如果這段代碼在非ARC情況下筹我,是這樣的:
{ id obj = [[NSObject alloc] init]; [obj release] }
也就是說(shuō):作用1:賦有_ _strong 修飾符的變量在obj超出其作用域的時(shí)候,強(qiáng)引用自動(dòng)失效帆离,引用計(jì)數(shù)自然歸0蔬蕊,釋放其被賦予的對(duì)象(可以理解為強(qiáng)引用加1,對(duì)象的引用計(jì)數(shù)就加1)
還有一點(diǎn)哥谷,以上是將使用alloc產(chǎn)生的對(duì)象岸夯,在前面已經(jīng)提到,這種情況下呼巷,obj是會(huì)自動(dòng)持有對(duì)象的囱修,其實(shí),在使用alloc/new/copy/mutablecopy以外的方式生成對(duì)象的時(shí)候王悍,如果添加描述符_ _strong ,此時(shí)obj自己就會(huì)持有對(duì)象了破镰,與之前不同:
{ id _ _strong obj = [NSMutableArray array]; //因?yàn)開 _strong為強(qiáng)引用,此時(shí)obj會(huì)自動(dòng)持有對(duì)象 } //obj超出作用域压储,強(qiáng)引用失效鲜漩,自動(dòng)釋放持有對(duì)象
另外,作用2:_ strong修飾符通后面要講的 weak修飾符集惋, _ autoreleasing修飾符一起孕似,可以保證附有這些修飾符的自動(dòng)變量在初始化的時(shí)候?yàn)閚il
id _ _strong obj0; id _ weak obj1; id _ autorelease obj2;
其實(shí)也就是:
id _ _strong obj0 = nil; id _ weak obj1 = nil; id _ autorelease obj2 = nil;
小結(jié)strong:對(duì)于之前在MRC中聲明的內(nèi)存管理原則中的“自己生成的對(duì)象自己持有”和“非自己生成的對(duì)象,自己也能持有”刮刑,通過(guò) _ _strong修飾符均可實(shí)現(xiàn)喉祭,通過(guò)廢棄 _ _strong修飾符的變量(變量作用域結(jié)束)或者對(duì)變量賦值,都可以實(shí)現(xiàn)雷绢,對(duì)于最后一項(xiàng):“不再需要時(shí)泛烙,主動(dòng)釋放”,很明顯翘紊,release就不需要了蔽氨,作用域結(jié)束時(shí)強(qiáng)引用失效,默認(rèn)實(shí)現(xiàn)了這一功能。
- _ _weak
使用_ weak的環(huán)境大伙兒都知道鹉究,是為了避免循環(huán)引用宇立,所以理解起來(lái)較為簡(jiǎn)單,此處僅簡(jiǎn)單總結(jié) _weak的作用即可
作用1:_ weak與 _strong修飾符相反自赔,提供弱引用妈嘹,弱引用不持有對(duì)象,也就是說(shuō)弱引用不能使對(duì)象的引用計(jì)數(shù)+1.
作用2:_ weak修飾符還有另一個(gè)優(yōu)點(diǎn)匿级,在持有某對(duì)象的弱引用蟋滴,若該對(duì)象被廢棄,則此弱引用將自動(dòng)失效且處于nil被賦值的狀態(tài)(空弱引用)痘绎,主要和后面的 _unself_unretained做對(duì)比津函,如下代碼所示:
id _ _weak obj1 = nil; { id _ _strong obj0 = [[NSObject alloc]init]; obj1 = obj0; NSLog(@"A:%@",obj1); } NSLog(@"B:%@",obj1);
打印結(jié)果如下:
A:<NSObject>: 0x753e180 B:(null)
_ weak小結(jié): weak修飾符只能用于iOS5以上,及OS X Lion以上版本的應(yīng)用程序孤页,在iOS4以及OS X Snow Leopard 的應(yīng)用程序中尔苦,可以使用 _unsafe_unretained修飾符代替(難道是它出現(xiàn)的原因?)
- _unsafe_unretained
_unsafe_unretained修飾符正如其名unsafe所示行施,是不安全的修飾符允坚,盡管ARC式的內(nèi)存管理是編譯器的工作,但附有_unsafe_unretained修飾符的變量不屬于編譯器的內(nèi)存管理對(duì)象蛾号。這一點(diǎn)使用時(shí)要注意稠项。
前文提到unsafe_unretained與 _weak功能相似,但是還是有差別鲜结,下面介紹:
id _ _weak obj1 = nil; { id _ _strong obj0 = [[NSObject alloc]init]; obj1 = obj0; NSLog(@"A:%@",obj1); } NSLog(@"B:%@",obj1); 打印結(jié)果如下: A:<NSObject>: 0x753e180 B:<NSObject>: 0x753e180
解釋:因?yàn)閛bj0變量超出其作用域展运,強(qiáng)引用失效,所以自動(dòng)釋放自己持有的對(duì)象精刷,所以整個(gè)對(duì)象無(wú)持有著拗胜,引用計(jì)數(shù)為0,所以廢棄怒允。但很顯_unsafe_unretained修飾的obj1在其指向的對(duì)象被廢棄了之后埂软,依然指向其地址,所以這就是問題(書上說(shuō)這叫懸垂指針纫事,估計(jì)就是咱們理解的野指針吧勘畔。)
unsafe_unretained小結(jié):在iOS4以及OS X Snow Leopard 的應(yīng)用程序中,務(wù)須使用 unsafe_unretained修飾符代替 _weak,所以在使用_unsafe_unretained的時(shí)候丽惶,雖然其指向地址有值炫七,但一定要確保其是否真實(shí)存在
- _ _autoreleasing
ARC有效時(shí),實(shí)際上之前使用的autorelease方法是不能使用的蚊夫,另外也不能使用NSAutoreleasePool類诉字。雖然在在ARC有效時(shí)懦尝,autorelease不能使用知纷,但其功能是可以通過(guò)其他方式來(lái)實(shí)現(xiàn)的壤圃,就如現(xiàn)在的_ _autoreleasing。
//ARC無(wú)效時(shí) NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; id obj = [[NSObject alloc]init]; [obj autorelease]; [pool drain]; //ARC有效時(shí)琅轧,上述源代碼就可以寫成如下了: @autoreleasePool { id _ _autoreleasing obj = [[NSObject alloc] init]; }
也就是說(shuō)伍绳,在ARC有效時(shí),用@autoreleasepool塊替代NSAutoreleasePool類乍桂,用附有_ _autoreleasing 修飾符替代autorelease方法
但是顯式的添加_ autorelease修飾符同顯式地附加 _strong一樣罕見
1.前面提到過(guò)冲杀,通過(guò)調(diào)用alloc/new/copy/mutablecopy以外的方法取得對(duì)象,對(duì)象會(huì)被自動(dòng)添加到autoreleasePool當(dāng)中睹酌,即使不在變量后添加_ _autoreleasing修飾符
2.對(duì)象作為函數(shù)的返回值权谁,編譯器會(huì)將其自動(dòng)添加到autoreleasePool(這一條不是很確定)下面簡(jiǎn)單舉例:
+(id) array{ id obj = [[NSObject alloc] init]; return obj; }
因?yàn)闆]有顯式添加所有權(quán)修飾符,所以obj其實(shí)是默認(rèn)添加了_ _ strong 的憋沿。由于return使得對(duì)象超出其作用域旺芽,所以該強(qiáng)引用對(duì)應(yīng)的自己持有的對(duì)象會(huì)被自動(dòng)釋放,但該對(duì)象作為返回值辐啄,編譯器會(huì)自動(dòng)的將其添加到autoreleasePool.
3.雖然_ weak修飾符是為了避免循環(huán)引用的采章,但其實(shí)訪問附有 _weak修飾符的變量時(shí),實(shí)際上必定要訪問到注冊(cè)到autoreleasePool的對(duì)象壶辜。
因?yàn)開 _weak修飾符只支持對(duì)象的弱引用悯舟,而在訪問引用對(duì)象的過(guò)程中,該對(duì)象可能隨時(shí)被廢棄砸民,如果把要訪問的對(duì)象注冊(cè)到autoreleasePool中,那么在@autoreleasepool塊結(jié)束之前都能確保該對(duì)象的存在抵怎,而不被釋放。
2.ARC規(guī)則
- 不能使用retain/release/retain/retainCount/autorelease
- 不能使用NSAllocateObject阱洪、NSDealloccateObject
- 須遵守內(nèi)存管理的方法命名規(guī)則
- 不要顯式調(diào)用dealloc
- 使用@autoreleasepool 塊替代NSAutorePool
- 不能使用區(qū)域NSZone
- 對(duì)象型變量不能作為C語(yǔ)言結(jié)構(gòu)體(struct/union)的成員
- 顯式轉(zhuǎn)換" id "和" void * "
下面簡(jiǎn)單介紹部分規(guī)則:
1.須遵守內(nèi)存管理的方法命名規(guī)則:在ARC有效時(shí)便贵,用于對(duì)象生成/持有方法必須遵守以下的命名規(guī)則
- alloc
- new
- copy
- mutableCopy
以上述名稱開始的方法在返回對(duì)象時(shí),必須返回給調(diào)用方所應(yīng)當(dāng)持有的對(duì)象冗荸。
2.對(duì)象型變量不能作為C語(yǔ)言結(jié)構(gòu)體(struct/union)的成員
struct Data { NSMutableArray *array; }
以上這種方式承璃,編譯就會(huì)報(bào)錯(cuò)。雖然是LLVM3.0蚌本,但是無(wú)論怎樣盔粹,C語(yǔ)言的規(guī)約上還是沒有方法來(lái)管理結(jié)構(gòu)體成員的生命周期。因?yàn)锳RC把內(nèi)存管理的工作分配給編譯器程癌,所以編譯器必須能夠知道并管理對(duì)象的生命周期舷嗡,但是對(duì)于C語(yǔ)言結(jié)構(gòu)體來(lái)說(shuō),這在標(biāo)準(zhǔn)上是不可實(shí)現(xiàn)的嵌莉。
要把對(duì)象類型的變量加入到結(jié)構(gòu)體成員中時(shí)进萄,可強(qiáng)制轉(zhuǎn)換為void *,或是附加前面所述的_ _unsafe_unretained修飾符即可。
3.ARC中的屬性
- 屬性種類:
屬性聲明種類 | 所有權(quán)修飾符 |
---|---|
assign | _ _unsafe_unretained |
copy | _ _strong修飾符(但是賦值的是被復(fù)制的對(duì)象) |
retain | _ _strong修飾符 |
strong | _ _strong修飾符 |
unsafe_unretained | _ _unsafe_unretained修飾符 |
weak | _ _weak修飾符 |
以上各種屬性賦值給指定的屬性中就相當(dāng)于賦值給附加各屬性對(duì)應(yīng)的所有權(quán)修飾符的變量。只有copy屬性不是簡(jiǎn)單的賦值中鼠,他賦值是通過(guò)NSCoping接口的copyWithZone方法復(fù)制賦值源所生成的對(duì)象可婶。
?? _ unsafe_unretained修飾符以外的 strong/ weak/ autorealease修飾符保證其指定的變量初始化為nil。同樣的援雇,附有 strong/ weak/ _autorealease修飾符變量的數(shù)組也可以保證其初始化為nil
4.ARC的實(shí)現(xiàn)(_ _strong)
蘋果官方說(shuō)明中稱矛渴,ARC是“由編譯器進(jìn)行內(nèi)存管理”的,但實(shí)際上只有編譯器是無(wú)法完全勝任的惫搏,在此基礎(chǔ)上還需要Objective-C運(yùn)行時(shí)的協(xié)助具温,也就是說(shuō),ARC由以下工具庫(kù)來(lái)實(shí)現(xiàn)(iOS5之后)
- clang(LLVM編譯器)3.0及以上
- obj4 Objective-C運(yùn)行時(shí)庫(kù)493.9以上
圍繞著clang匯編輸出和objc4庫(kù)(主要是runtime/objc-arr.mm)的源代碼進(jìn)行說(shuō)明
-
_ strong修飾符(研究賦值給附有 _strong修飾符的變量在實(shí)際的程序中到底是怎么運(yùn)行的)
{ id _ _strong obj = [[NSObject alloc] init]; }
??在編譯器選項(xiàng)“-S”的同時(shí)運(yùn)行clang筐赔,可取得程序匯編輸出铣猩,看看匯編輸出和obj4庫(kù)的源代碼就能知道程序是如何工作的。該源代碼實(shí)際上可以轉(zhuǎn)化為調(diào)用以下的函數(shù)茴丰,為了方便理解剂习,都將使用模擬源代碼。
//編譯器模擬的代碼 id obj = objc_msgSend(NSObject,@selector(alloc)); objc_msgSend(obj,@selector(init)); objc_release(obj);
??
如源代碼所示较沪,2次調(diào)用objc_msgSend方法(alloc和init方法)鳞绕,變量作用域結(jié)束時(shí),通過(guò)objc_release釋放對(duì)象尸曼。雖然ARC有效時(shí)不能使用release方法们何,但由此推測(cè)編譯器編譯時(shí)自動(dòng)插入了release
參考文獻(xiàn):
- 《Pro multithreading and memory management for iOS and OS X》