引用計數(shù)與ARC

引用計數(shù):一個簡單而有效的管理對象生命周期的方式,Objective-C和Swift的內(nèi)存管理方式都是基于引用計數(shù)的随珠。
引用計數(shù)的原理:當(dāng)創(chuàng)建一個對象蝶俱,它的引用計數(shù)為1笛匙,當(dāng)有一個新的指針只想這個對象侨把,它的引用計數(shù)+1,當(dāng)某個指針不再指向這個對象時妹孙,我們將其引用計數(shù)-1秋柄,當(dāng)對象的引用計數(shù)為0時,說明這個對象不再被任何指針指向了涕蜂,這時候华匾,就可以將這個對象銷毀,回收內(nèi)存机隙。

NSObject *object = [[NSObject alloc]init]; //創(chuàng)建對象 引用計數(shù) +1 
NSLog(@"Reference Count = %u",[object retainCount]); // Reference Count = 1
[object release]; //引用計數(shù) -1 對象會被釋放掉蜘拉。

為什么要使用引用計數(shù)?

  • 當(dāng)我們在一個函數(shù)內(nèi)使用一個臨時對象時有鹿,通常不需要修改它的引用計數(shù)旭旭,只需要在函數(shù)返回前將該對象銷毀即可。
  • 引用計數(shù)正真排上用場的是在面向?qū)ο蟮某绦虻脑O(shè)計架構(gòu)中葱跋,用于對象之間傳遞和共享數(shù)據(jù)持寄。

舉個例子:
當(dāng)對象A生成了一個對象M源梭,需要調(diào)用對象B的一個方法,將對象M作為參數(shù)傳給對象B稍味。在沒有引用計數(shù)的情況下废麻,一般內(nèi)存管理的原則是“誰申請誰釋放”,那么對象A就需要在對B不需要對象M的時候講M銷毀模庐,但對象B可能只是臨時用一下對象M烛愧,也可能覺得對象M也很重要,講它設(shè)置成自己的一個成員變量掂碱,在這種情況下怜姿,就不好確定什么時候該釋放對象M。

  • 方法一:暴力釋放:就是在在A對象調(diào)用完對象B后疼燥,立馬銷毀對象M沧卢,然后B再復(fù)制一份對象M生成M2,然后自己管理M2的生命周期醉者,但是這樣做就會有一個很大的問題但狭,內(nèi)存申請、復(fù)制湃交、釋放的工作會耗費很多的性能熟空。本來一個可以復(fù)用的對象,因為不方便管理其生命周期搞莺,就簡單的把它銷毀,又重新構(gòu)造一份一模一樣的掂咒,實在很影響性能才沧。
  • 方法二: 對象A在構(gòu)造玩對象M后,始終不銷毀對象M绍刮,由對象B去完成對象M的銷毀工作温圆,如果對象B需要長時間使用對象M就不銷毀它。如果只是臨時用一下孩革,就可以用完立馬銷毀岁歉。雖然看似很好的解決了對象復(fù)制的問題,但是它強烈的依賴于A和B兩個對象的配合膝蜈,代碼維護者需要明確的記住這種編程約定锅移。而且,由于對象M的申請實在對象A中饱搏,釋放在對象B中非剃,是的它的內(nèi)存管理代碼非常的分散于不同的對象中,管理起來十分的費勁推沸。如果這時候再復(fù)雜一點备绽,對象B需要向C也傳遞對象M券坞,那么這個時候?qū)ο驝中又不能讓對象C管理。所以這種方式帶來的復(fù)雜性更大肺素,更不可取恨锚。

所以引用計數(shù)就很好的解決了這個問題,哪些對象需要長時間的使用這個對象倍靡,就把它的引用計數(shù)+1猴伶,使用完之后-1,。所有的對象都遵循這個規(guī)則的話菌瘫,對象的生命周期就可以完全交給引用計數(shù)了蜗顽。我們也可以方便的享受對象帶來的好處

不要向已經(jīng)釋放的對象發(fā)送消息
一個對象如果已經(jīng)被釋放回收,它所占的內(nèi)容被復(fù)用了雨让,那么就會造成程序異常崩潰雇盖。

NSObject *object = [[NSObject alloc]init]; //創(chuàng)建對象 引用計數(shù) +1 
NSLog(@"Reference Count = %u",[object retainCount]); // Reference Count = 1
[object release]; //引用計數(shù) -1 對象會被釋放掉。
NSLog(@"Reference Count = %u",[object retainCount]); // Reference Count = 1

上面這段代碼中兩次打印結(jié)果都是 Reference Count = 1栖忠,因為當(dāng)系統(tǒng)最后一次執(zhí)行release的時候崔挖,系統(tǒng)已經(jīng)知道了你這個對象會被立馬釋放,所以沒有必要再將retainCount 減 1 了庵寞,因為不管減不減 1 都會被釋放回收狸相,所以對應(yīng)該對象的內(nèi)存區(qū)域和retainCount的值也就變的毫無意義。不將這個值從1變成0捐川,反而可以減少一次內(nèi)存操作脓鹃,加速對象的回收。對于內(nèi)存古沥,我們要錙銖必較瘸右。

循環(huán)引用
雖然引用計數(shù)這種管理內(nèi)存的方式很簡單,但是有一個較為大的問題就是岩齿,它不能解決循環(huán)引用太颤。在block的使用中經(jīng)常會遇到循環(huán)引用的問題。

舉個例子:

  • A盹沈、B連個對象龄章,相互引用了對方作為自己的成員變量。只有自己銷毀時乞封,才將成員變量的引用計數(shù)減1.因為A的銷毀依賴于B的銷毀做裙,同樣的,B的銷毀也依賴于A的銷毀歌亲。這樣就造成了死循環(huán)一樣菇用,即使外界已經(jīng)沒有人持有它們了,但是它們還是不能夠銷毀釋放陷揪,這就是 循環(huán)引用惋鸥。
  • 不止兩個對象會造成循環(huán)引用杂穷,多個對象也會造成循環(huán)引用,形成類似一個閉環(huán)卦绣,多個對象依次持有耐量。在我們的實際開發(fā)中,當(dāng)這個閉環(huán)越大的時候滤港,就越難發(fā)現(xiàn)廊蜒。

如何解決循環(huán)引用呢?

  1. 明確知道會在這里造成循環(huán)引用溅漾,根據(jù)具體業(yè)務(wù)邏輯在不需要的時候山叮,合理的位置上主動斷開閉環(huán)中的引用,使得對象回收添履。 這種方式需要程序員顯示的手動去釋放對象屁倔,相當(dāng)于回到以前的“誰創(chuàng)建誰釋放”的內(nèi)存管理年代,它就需要程序員自己有能去發(fā)現(xiàn)循環(huán)引用暮胧,并且知道在什么時機斷開循環(huán)引用回收內(nèi)存锐借。比較不常用。
  2. 弱引用:弱引用往衷,雖然持有對象钞翔,但是不會增加引用計數(shù),這樣就避免了循環(huán)引用的問題席舍。在iOS開發(fā)中布轿,弱引用通常運用在delegate和block中。
    在Xocede中自帶有檢測循環(huán)引用的工具来颤。Xcode的工具集Instruments可以很方便的檢測循環(huán)引用問題驮捍。
    操作步驟:菜單欄 Product → Profile,程序運行完后選擇 Leaks ,choose選擇脚曾。
    image.png

ARC

  • ARC(Automatic Reference Count 簡稱ARC)自動引用計數(shù),用于內(nèi)存管理的技術(shù)启具。ARC是WWDC2011大會提出來的技術(shù)本讥,蘋果將OSX上的垃圾回收機制飛起,采用ARC替代鲁冯,現(xiàn)在的ARC技術(shù)已經(jīng)很成熟了拷沸。筆者使用ARC的這幾年,基本沒有發(fā)生過內(nèi)存泄漏問題薯演。

  • ARC并不是GC(Garbage Collection 垃圾回收器)撞芍,它只是一種代碼靜態(tài)分析(Static Analyzer)工具,背后的原理是依賴編譯器的靜態(tài)分析能力跨扮,通過在編譯時找出合理的插入引用計數(shù)管理代碼序无,從而提高iOS開發(fā)人員的開發(fā)效率验毡。

  • Apple的文檔里是這么定義ARC的:“自動引用計數(shù)(ARC)是一個編譯器級的功能,它能簡化Cocoa應(yīng)用中對象生命周期管理(內(nèi)存管理)的流程帝嗡。

  • ARC的實現(xiàn)機制是在編譯期完成的晶通。在編譯階段,編譯器將在項目代碼中自動為分配對象插入retain哟玷、release和autorelease狮辽,且插入的代碼不可見。

作用:

-.降低內(nèi)存泄露等風(fēng)險 巢寡;
-.減少代碼工作量喉脖,使開發(fā)者只需專注于業(yè)務(wù)邏輯

特別需要注意的

  • ARC確實可以解決90%以上的內(nèi)存管理問題,但是還是有10%需要開發(fā)者自己處理的抑月,尤為要提的就是與底層 Core Foundation 對象交互的那部分树叽,底層的 Core Foundation 對象不在ARC的管理下,所以需要手動處理這些對象的引用計數(shù)爪幻。
  • 還有一個就是在過度使用block的時候菱皆,會出現(xiàn)循環(huán)引用的問題,但是又不能解決挨稿。
    Core Foundation :
//創(chuàng)建一個CFStringRef對象
CFStringRef str = CFStringCreateWithCString(KCFAllocatorDefault, "Hello world",KCFStringEncodingUTF8);
//創(chuàng)建一個CTFontRef對象
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)@"AriaMT",fontSize,NULL);
//如果要對這些對象的引用計數(shù)做修改就要使用對應(yīng)的 CFRetain 和 CFRelease 方法仇轻。
CFRetain(fonnRef) //引用計數(shù) +1
CFRelease(fontRef) //引用計數(shù) -1

當(dāng)在ARC模式下使用 Core Foundation 時,我們需要將一個CF對象轉(zhuǎn)成OC對象的時候奶甘,需要告訴編譯器篷店,轉(zhuǎn)換過程中的引用計數(shù)如何調(diào)整。在轉(zhuǎn)換時需要引入一些關(guān)鍵字:

  • __bridge:只做類型轉(zhuǎn)換臭家,不修改相關(guān)的引用計數(shù)疲陕。原理的CF對象不在時,需要調(diào)用CFRelease钉赁。(反之亦然)
  • __bridge_retained:類型轉(zhuǎn)換后蹄殃,將相關(guān)對象引用計數(shù)加1,原來的CF對象不在時你踩,需要調(diào)用CFRelease诅岩。
  • __bridge_transfer:類型轉(zhuǎn)換后,將該對象的引用計數(shù)交給ARC管理带膜,CF對象不存在時吩谦,不需要調(diào)用CFRelease。
    所以膝藕,我們需要根據(jù)具體的業(yè)務(wù)邏輯式廷,合理使用上面這三種轉(zhuǎn)換的關(guān)鍵字,就可以解決Core Foundation 與 Objective-C 對象相互轉(zhuǎn)換的問題了芭挽。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末滑废,一起剝皮案震驚了整個濱河市蝗肪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌策严,老刑警劉巖穗慕,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異妻导,居然都是意外死亡逛绵,警方通過查閱死者的電腦和手機躬翁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門阁最,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人憋肖,你說我怎么就攤上這事寿酌∫人眨” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵醇疼,是天一觀的道長硕并。 經(jīng)常有香客問我,道長秧荆,這世上最難降的妖魔是什么倔毙? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮乙濒,結(jié)果婚禮上陕赃,老公的妹妹穿的比我還像新娘。我一直安慰自己颁股,他們只是感情好么库,可當(dāng)我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著甘有,像睡著了一般诉儒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上亏掀,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天允睹,我揣著相機與錄音,去河邊找鬼幌氮。 笑死,一個胖子當(dāng)著我的面吹牛胁澳,可吹牛的內(nèi)容都是我干的该互。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼韭畸,長吁一口氣:“原來是場噩夢啊……” “哼宇智!你這毒婦竟也來了蔓搞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤随橘,失蹤者是張志新(化名)和其女友劉穎喂分,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體机蔗,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡蒲祈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了萝嘁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梆掸。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖牙言,靈堂內(nèi)的尸體忽然破棺而出酸钦,到底是詐尸還是另有隱情,我是刑警寧澤咱枉,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布卑硫,位于F島的核電站,受9級特大地震影響蚕断,放射性物質(zhì)發(fā)生泄漏欢伏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一基括、第九天 我趴在偏房一處隱蔽的房頂上張望颜懊。 院中可真熱鬧,春花似錦风皿、人聲如沸河爹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咸这。三九已至,卻和暖如春魔眨,著一層夾襖步出監(jiān)牢的瞬間媳维,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工遏暴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留侄刽,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓朋凉,卻偏偏與公主長得像州丹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,527評論 2 349

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