我的iOS內(nèi)存管理-MRC-ARC

貌似每個(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ì)象挨厚,自己也能持有
    通過(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方法

對(duì)應(yīng)圖.png

但是顯式的添加_ 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》
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市控轿,隨后出現(xiàn)的幾起案子冤竹,更是在濱河造成了極大的恐慌,老刑警劉巖茬射,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鹦蠕,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡在抛,警方通過(guò)查閱死者的電腦和手機(jī)钟病,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)刚梭,“玉大人肠阱,你說(shuō)我怎么就攤上這事∑佣粒” “怎么了屹徘?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)衅金。 經(jīng)常有香客問我噪伊,道長(zhǎng)簿煌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任鉴吹,我火速辦了婚禮啦吧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拙寡。我一直安慰自己,他們只是感情好琳水,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布肆糕。 她就那樣靜靜地躺著,像睡著了一般在孝。 火紅的嫁衣襯著肌膚如雪诚啃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天私沮,我揣著相機(jī)與錄音始赎,去河邊找鬼。 笑死仔燕,一個(gè)胖子當(dāng)著我的面吹牛造垛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播晰搀,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼五辽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了外恕?” 一聲冷哼從身側(cè)響起杆逗,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鳞疲,沒想到半個(gè)月后罪郊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尚洽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年悔橄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腺毫。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡橄维,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拴曲,到底是詐尸還是另有隱情争舞,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布澈灼,位于F島的核電站竞川,受9級(jí)特大地震影響店溢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜委乌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一床牧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧遭贸,春花似錦戈咳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至耳贬,卻和暖如春踏堡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咒劲。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工顷蟆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腐魂。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓帐偎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蛔屹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肮街,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容