iOS內(nèi)存管理

Objective-C,顧名思義卵慰,是一門超C的語言缩歪,自從ARC(Auto Reference Count)出現(xiàn)了之后,我們就很少會(huì)去關(guān)注關(guān)于內(nèi)存管理這方面的事情了罩句,這些功能的設(shè)計(jì)者和實(shí)現(xiàn)者們?yōu)榇烁冻龅呐χ档梦覀兎Q贊焚刺,但是如果我們對(duì)此不知不顧的話,一旦出現(xiàn)了內(nèi)存泄漏的問題的話门烂,也是個(gè)坑乳愉,所以先去嘗試著去了解它兄淫,對(duì)我們也不失是一件好事。下面是c語言中的內(nèi)存分配方式:

棧區(qū)(stack):棧又稱堆棧蔓姚, 是用戶存放程序臨時(shí)創(chuàng)建的局部變量捕虽,也就是說我們函數(shù)括弧“{}”中定義的變量。除此以外坡脐,在函數(shù)被調(diào)用時(shí)泄私,其參數(shù)也會(huì)被壓入發(fā)起調(diào)用的進(jìn)程棧中,并且待到調(diào)用結(jié)束后备闲,函數(shù)的返回值也會(huì)被存放回棧中晌端。由于棧的先進(jìn)先出特點(diǎn),所以棧特別方便用來保存/恢復(fù)調(diào)用現(xiàn)場(chǎng)恬砂。從這個(gè)意義上講斩松,我們可以把堆棧看成一個(gè)寄存觉既、交換臨時(shí)數(shù)據(jù)的內(nèi)存區(qū)惧盹。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集,效率很高瞪讼,但是分配的內(nèi)存容量有限钧椰,比如iOS中棧區(qū)的大小是2M。(棧系統(tǒng)提供的功能符欠,特點(diǎn)是快速高效嫡霞,缺點(diǎn)是有限制,數(shù)據(jù)不靈活希柿;而堆是函數(shù)庫(kù)提供的功能诊沪,特點(diǎn)是靈活方便,數(shù)據(jù)適應(yīng)面廣泛曾撤,但是效率有一定降低端姚。)

堆區(qū)(heap):就是通過new、malloc挤悉、realloc分配的內(nèi)存塊渐裸,它們的釋放編譯器不去管,由我們的應(yīng)用程序去釋放装悲。如果應(yīng)用程序沒有釋放掉昏鹃,操作系統(tǒng)會(huì)自動(dòng)回收。分配方式類似于鏈表诀诊。

靜態(tài)區(qū):全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的洞渤,初始化的全局變量和靜態(tài)變量在一塊區(qū)域,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域属瓣。程序結(jié)束后载迄,由系統(tǒng)釋放奈懒。

常量區(qū):常量存儲(chǔ)在這里,不允許修改的宪巨。

代碼區(qū):存放函數(shù)體的二進(jìn)制代碼。

iOS開發(fā)中溜畅,數(shù)據(jù)類型有兩種捏卓,一種是int 、float等基本數(shù)據(jù)類型慈格,而基本數(shù)據(jù)類型是放在棧上怠晴,由系統(tǒng)進(jìn)行管理,還有一種就是對(duì)象浴捆,存儲(chǔ)在堆上蒜田,如果一個(gè)對(duì)象創(chuàng)建并使用后并沒有得到及時(shí)的釋放,就會(huì)一直占用著內(nèi)存选泻,當(dāng)這樣的對(duì)象一直占據(jù)著內(nèi)存得不到釋放冲粤,這就是我們常說的內(nèi)存泄漏,嚴(yán)重的話會(huì)導(dǎo)致程序崩潰页眯。Objective-C 中的內(nèi)存管理梯捕,其實(shí)主要是講的在堆區(qū)上面對(duì)內(nèi)存的管理,而iOS的內(nèi)存管理機(jī)制就是引用計(jì)數(shù)(reference counting):

1)每個(gè)對(duì)象都有一個(gè)關(guān)聯(lián)的整數(shù)窝撵,稱為引用計(jì)數(shù)器

2)當(dāng)代碼需要使用該對(duì)象時(shí)傀顾,則將對(duì)象的引用計(jì)數(shù)加1

3)當(dāng)代碼結(jié)束使用該對(duì)象時(shí),則將對(duì)象的引用計(jì)數(shù)減1

4)當(dāng)引用計(jì)數(shù)的值變?yōu)?時(shí)碌奉,表示對(duì)象沒有被任何代碼使用短曾,此時(shí)對(duì)象將被釋放。

其實(shí)對(duì)應(yīng)的也可以用開關(guān)房間的燈為例來說明引用計(jì)數(shù)的機(jī)制赐劣。

1)當(dāng)?shù)谝粋€(gè)人進(jìn)入辦公室時(shí)嫉拐,他需要使用燈,于是開燈魁兼,引用計(jì)數(shù)為1

2)每當(dāng)多一個(gè)人進(jìn)入辦公室時(shí)椭岩,引用計(jì)數(shù)加1

3)當(dāng)有一個(gè)人離開辦公室時(shí),引用計(jì)數(shù)減1璃赡,

4)當(dāng)引用計(jì)數(shù)為0時(shí)判哥,也就是最后一個(gè)人離開辦公室時(shí),他不再需要使用燈碉考,關(guān)燈離開辦公室塌计。(釋放對(duì)象)

1. Objective-c語言中的MRC(MannulReference Counting)

在MRC的內(nèi)存管理模式下,與對(duì)變量的管理相關(guān)的方法有:retain,release和autorelease侯谁。retain和release方法操作的是引用記數(shù)锌仅,當(dāng)引用記數(shù)為零時(shí)章钾,便自動(dòng)釋放內(nèi)存。并且可以用NSAutoreleasePool對(duì)象热芹,對(duì)加入自動(dòng)釋放池(autorelease調(diào)用)的變量進(jìn)行管理贱傀,當(dāng)drain時(shí)回收內(nèi)存。

1)retain伊脓,retain是一個(gè)實(shí)例方法府寒,只能由對(duì)象調(diào)用,它的作用是使這個(gè)對(duì)象的內(nèi)存空間的引用計(jì)數(shù)加1报腔,并不會(huì)新開辟一塊內(nèi)存空間株搔,通常于賦值是調(diào)用,該方法的作用是將內(nèi)存數(shù)據(jù)的所有權(quán)附給另一指針變量纯蛾,引用數(shù)加1纤房,即retainCount+= 1 ; 如:對(duì)象2=[對(duì)象1 retain];表示對(duì)象2同樣擁有這塊內(nèi)存的所有權(quán)翻诉。若只是簡(jiǎn)單地賦值炮姨,如:對(duì)象2=對(duì)象1;那么當(dāng)對(duì)象1的內(nèi)存空間被釋放的時(shí)候碰煌,對(duì)象2便會(huì)成為野指針剑令,再對(duì)對(duì)象2進(jìn)行操作便會(huì)造成內(nèi)存錯(cuò)誤。

2)release拄查,該方法是釋放指針變量對(duì)內(nèi)存數(shù)據(jù)的所有權(quán)吁津,引用數(shù)減1,即retainCount-= 1堕扶,若引用計(jì)數(shù)變?yōu)?則系統(tǒng)會(huì)立刻釋放掉這塊內(nèi)存碍脏。如果引用計(jì)數(shù)為0的基礎(chǔ)上再調(diào)用release,便會(huì)造成過度釋放稍算,使內(nèi)存崩潰典尾;

3)autorelease,:autorelease是一個(gè)實(shí)例方法糊探,同樣只能由對(duì)象調(diào)用钾埂,它的作用于release類似,但不是立刻減1科平,相當(dāng)于一個(gè)延遲的release褥紫,通常用于方法返回值的釋放,如便利構(gòu)造器瞪慧。autorelease會(huì)在程序走出自動(dòng)釋放池時(shí)執(zhí)行髓考,通常系統(tǒng)會(huì)自動(dòng)生成自動(dòng)釋放池(即使是MRC下),也可以自己設(shè)定自動(dòng)釋放池弃酌,如:

@autoreleasepool{

? ? ? ? ?obj= [[NSObject alloc]init];

? ? ? ? ?[obj autorelease];

}

當(dāng)程序走出“}”時(shí)obj的引用計(jì)數(shù)就會(huì)減1.

當(dāng)然自動(dòng)釋放池還有一種寫法

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init];

//這里寫代碼

[pool release];

但是使用自動(dòng)釋放池需要注意:

1)自動(dòng)釋放池實(shí)質(zhì)上只是在釋放的時(shí)候給池中所有對(duì)象對(duì)象發(fā)送release消息氨菇,不保證對(duì)象一定會(huì)銷毀儡炼,如果自動(dòng)釋放池向?qū)ο蟀l(fā)送release消息后對(duì)象的引用計(jì)數(shù)仍大于1,對(duì)象就無法銷毀查蓉。

2)自動(dòng)釋放池中的對(duì)象會(huì)集中同一時(shí)間釋放乌询,如果操作需要生成的對(duì)象較多占用內(nèi)存空間大,可以使用多個(gè)釋放池來進(jìn)行優(yōu)化豌研。比如在一個(gè)循環(huán)中需要?jiǎng)?chuàng)建大量的臨時(shí)變量妹田,可以創(chuàng)建內(nèi)部的池子來降低內(nèi)存占用峰值。

3)autorelease不會(huì)改變對(duì)象的引用計(jì)數(shù)

在mrc中程序員們要遵循下面的基本原則:

1)當(dāng)你通過new聂沙、alloc、copy或mutableCopy方法創(chuàng)建一個(gè)對(duì)象時(shí)初嘹,它的引用計(jì)數(shù)為1及汉,當(dāng)不再使用該對(duì)象時(shí),應(yīng)該向?qū)ο蟀l(fā)送release或者autorelease消息釋放對(duì)象屯烦。

2)當(dāng)你通過其他方法獲得一個(gè)對(duì)象時(shí)坷随,如果對(duì)象引用計(jì)數(shù)為1且被設(shè)置為autorelease,則不需要執(zhí)行任何釋放對(duì)象的操作驻龟;

3)如果你打算取得對(duì)象所有權(quán)温眉,就需要保留對(duì)象并在操作完成之后釋放,且必須保證retain和release的次數(shù)對(duì)等翁狐。

簡(jiǎn)單得來說就是誰分配誰釋放类溢。

2. Objective-c語言中的ARC下的內(nèi)存管理(Automatic Reference Counting)

ARC是iOS 5推出的新功能,全稱叫 ARC(Automatic Reference Counting)露懒。簡(jiǎn)單地說闯冷,就是代碼中自動(dòng)加入了retain/release,原先需要手動(dòng)添加的用來處理內(nèi)存管理的引用計(jì)數(shù)的代碼可以自動(dòng)地由編譯器完成了懈词。該機(jī)能在 iOS 5/ Mac OS X 10.7 開始導(dǎo)入蛇耀,利用 Xcode4.2 可以使用該機(jī)能。簡(jiǎn)單地理解ARC坎弯,就是通過指定的語法纺涤,讓編譯器(LLVM 3.0)在編譯代碼時(shí),自動(dòng)生成實(shí)例的引用計(jì)數(shù)管理部分代碼抠忘。有一點(diǎn)撩炊,ARC并不是GC,它只是一種代碼靜態(tài)分析(Static Analyzer)工具崎脉。

使用ARC有什么好處呢衰抑?

其實(shí)大家都知道,自從有了arc之后寫Objective-C的代碼變得簡(jiǎn)單多了荧嵌,因?yàn)槲覀儾恍枰獡?dān)心煩人的內(nèi)存管理呛踊,擔(dān)心內(nèi)存泄露了

代碼的總量變少了砾淌,看上去清爽了不少,也節(jié)省了勞動(dòng)力

代碼高速化谭网,由于使用編譯器管理引用計(jì)數(shù)汪厨,減少了低效代碼的可能性

但是實(shí)質(zhì)上ARC只能解決iOS開發(fā)中的90%的內(nèi)存管理問題,還有10%的內(nèi)存管理是需要程序員自己處理的愉择,主要是循環(huán)引用和對(duì)底層Core Foundatin的調(diào)用劫乱。

循環(huán)引用,其實(shí)簡(jiǎn)單得來講就是對(duì)象A和對(duì)象B锥涕,互相引用對(duì)方作為自己的成員變量衷戈,但是由于引用計(jì)數(shù)的管理方式,只有對(duì)象自己銷毀的時(shí)候层坠,才會(huì)將成員變量的引用計(jì)數(shù)減1殖妇,然而對(duì)象A和對(duì)象B由于都引用了對(duì)方,都得不到銷毀破花,這樣的情況就是循環(huán)引用谦趣。當(dāng)然這只是循環(huán)引用的一種情況,多個(gè)對(duì)象依次持有對(duì)方座每,形成一個(gè)環(huán)狀前鹅,也可能造成循環(huán)引用,一般來說峭梳,環(huán)越大就越難被發(fā)現(xiàn)舰绘。

一般來說,解決循環(huán)引用的方法有兩個(gè)

1)第一個(gè)方法是我知道這里會(huì)造成循環(huán)引用葱椭,但是我在使用完了這個(gè)功能除盏、方法之后我主動(dòng)去破除這個(gè)循環(huán)引用的情況,主動(dòng)斷開循環(huán)引用這種操作依賴于程序員自己手工顯式地控制挫以,相當(dāng)于回到了以前 “誰申請(qǐng)誰釋放” 的內(nèi)存管理年代者蠕,它依賴于程序員自己有能力發(fā)現(xiàn)循環(huán)引用并且知道在什么時(shí)機(jī)斷開循環(huán)引用回收內(nèi)存(這通常與具體的業(yè)務(wù)邏輯相關(guān)),所以這種解決方法并不常用掐松,更常見的辦法是使用弱引用 (weak reference) 的辦法踱侣。

2)弱引用雖然持有對(duì)象,但是并不增加引用計(jì)數(shù)大磺,這樣就避免了循環(huán)引用的產(chǎn)生抡句。在 iOS 開發(fā)中,弱引用通常在 delegate 模式中使用杠愧。弱引用的實(shí)現(xiàn)原理是這樣待榔,系統(tǒng)對(duì)于每一個(gè)有弱引用的對(duì)象,都維護(hù)一個(gè)表來記錄它所有的弱引用的指針地址。這樣锐锣,當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)為 0 時(shí)腌闯,系統(tǒng)就通過這張表,找到所有的弱引用指針雕憔,繼而把它們都置成 nil姿骏。一般在block中使用不當(dāng)也會(huì)造成循環(huán)引用,例如在使用MJRefresh這個(gè)常用的刷新第三方庫(kù)的時(shí)候斤彼,要使用__weak 來實(shí)現(xiàn)弱引用分瘦,避免循環(huán)引用

__weak typeof(self) weakself = self;

self.table.mj_header = [MJRefreshGifHeader headerWithRefreshingBlock:^{

? ? ? ?weakself.page = 1;

? ? ? ?weakself.table.mj_footer.hidden = YES;

? ? ? ?[weakself sendRequest];

}];

Core Foundation 對(duì)象的內(nèi)存管理

下面我們就來簡(jiǎn)單介紹一下對(duì)底層 Core Foundation 對(duì)象的內(nèi)存管理。底層的 Core Foundation 對(duì)象琉苇,在創(chuàng)建時(shí)大多以 XxxCreateWithXxx 這樣的方式創(chuàng)建嘲玫,例如:

// 創(chuàng)建一個(gè) CFStringRef 對(duì)象

CFStringRef str= CFStringCreateWithCString(kCFAllocatorDefault, “hello world", kCFStringEncodingUTF8);

// 創(chuàng)建一個(gè) CTFontRef 對(duì)象

CTFontRef fontRef = CTFontCreateWithName((CFStringRef)@"ArialMT", fontSize, NULL);

對(duì)于這些對(duì)象的引用計(jì)數(shù)的修改,要相應(yīng)的使用 CFRetain 和 CFRelease 方法并扇。如下所示:

// 創(chuàng)建一個(gè) CTFontRef 對(duì)象

CTFontRef fontRef = CTFontCreateWithName((CFStringRef)@"ArialMT", fontSize, NULL);

// 引用計(jì)數(shù)加 1

CFRetain(fontRef);

// 引用計(jì)數(shù)減 1

CFRelease(fontRef);

對(duì)于 CFRetain 和 CFRelease 兩個(gè)方法去团,讀者可以直觀地認(rèn)為,這與 Objective-C 對(duì)象的 retain 和 release 方法等價(jià)拜马。

所以對(duì)于底層 Core Foundation 對(duì)象渗勘,我們只需要延續(xù)以前手工管理引用計(jì)數(shù)的辦法即可沐绒。

除此之外俩莽,還有另外一個(gè)問題需要解決。在 ARC 下乔遮,我們有時(shí)需要將一個(gè) Core Foundation 對(duì)象轉(zhuǎn)換成一個(gè) Objective-C 對(duì)象扮超,這個(gè)時(shí)候我們需要告訴編譯器,轉(zhuǎn)換過程中的引用計(jì)數(shù)需要做如何的調(diào)整蹋肮。這就引入了bridge相關(guān)的關(guān)鍵字出刷,以下是這些關(guān)鍵字的說明:

__bridge: 只做類型轉(zhuǎn)換,不修改相關(guān)對(duì)象的引用計(jì)數(shù)坯辩,原來的 Core Foundation 對(duì)象在不用時(shí)馁龟,需要調(diào)用 CFRelease 方法。

__bridge_retained:類型轉(zhuǎn)換后漆魔,將相關(guān)對(duì)象的引用計(jì)數(shù)加 1坷檩,原來的 Core Foundation 對(duì)象在不用時(shí),需要調(diào)用 CFRelease 方法改抡。

__bridge_transfer:類型轉(zhuǎn)換后矢炼,將該對(duì)象的引用計(jì)數(shù)交給 ARC 管理,Core Foundation 對(duì)象在不用時(shí)阿纤,不再需要調(diào)用 CFRelease 方法句灌。

我們根據(jù)具體的業(yè)務(wù)邏輯,合理使用上面的 3 種轉(zhuǎn)換關(guān)鍵字欠拾,就可以解決 Core Foundation 對(duì)象與 Objective-C 對(duì)象相對(duì)轉(zhuǎn)換的問題了胰锌。

在項(xiàng)目中骗绕,其實(shí)我們也可以用Instruments來檢測(cè)我們項(xiàng)目的內(nèi)存泄漏的問題。

參考文章:

1理解 iOS 的內(nèi)存管理

2iOS內(nèi)功篇:內(nèi)存管理

3iOS內(nèi)存管理:基本概念與原理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末匕荸,一起剝皮案震驚了整個(gè)濱河市爹谭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌榛搔,老刑警劉巖诺凡,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異践惑,居然都是意外死亡腹泌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門尔觉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凉袱,“玉大人,你說我怎么就攤上這事侦铜∽ㄋΓ” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵钉稍,是天一觀的道長(zhǎng)涤躲。 經(jīng)常有香客問我,道長(zhǎng)贡未,這世上最難降的妖魔是什么种樱? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮俊卤,結(jié)果婚禮上嫩挤,老公的妹妹穿的比我還像新娘。我一直安慰自己消恍,他們只是感情好岂昭,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著狠怨,像睡著了一般约啊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上取董,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天棍苹,我揣著相機(jī)與錄音,去河邊找鬼茵汰。 笑死枢里,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播栏豺,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼彬碱,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了奥洼?” 一聲冷哼從身側(cè)響起巷疼,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎灵奖,沒想到半個(gè)月后嚼沿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瓷患,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年骡尽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片擅编。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡攀细,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出爱态,到底是詐尸還是另有隱情谭贪,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布锦担,位于F島的核電站俭识,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏吆豹。R本人自食惡果不足惜鱼的,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一理盆、第九天 我趴在偏房一處隱蔽的房頂上張望痘煤。 院中可真熱鬧,春花似錦猿规、人聲如沸衷快。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蘸拔。三九已至,卻和暖如春环葵,著一層夾襖步出監(jiān)牢的瞬間调窍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工张遭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留邓萨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像缔恳,于是被迫代替她去往敵國(guó)和親宝剖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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