Objective-C 內(nèi)存管理

Objective-C 內(nèi)存管理

在 Objective-C 中留量,對象通常是使用 alloc 方法在堆上創(chuàng)建的搀缠。 [NSObject alloc] 方法會在對堆上分配一塊內(nèi)存旺隙,按照NSObject的內(nèi)部結(jié)構(gòu)填充這塊兒內(nèi)存區(qū)域。

一旦對象創(chuàng)建完成穆刻,就不可能再移動它了。因為很可能有很多指針都指向這個對象妄讯,這些指針并沒有被追蹤。因此沒有辦法在移動對象的位置之后更新全部的這些指針酷宵。

MRC 與 ARC


Objective-C中提供了兩種內(nèi)存管理機制:MRC(MannulReferenceCounting)和 ARC(Automatic Reference Counting)亥贸,分別提供對內(nèi)存的手動和自動管理,來滿足不同的需求〗娇眩現(xiàn)在蘋果推薦使用 ARC 來進行內(nèi)存管理炕置。

MRC

  • autorelease 使得對象在超出生命周期后能正確的被釋放(通過調(diào)用release方法)。在調(diào)用 release 后男韧,對象會被立即釋放朴摊,而調(diào)用 autorelease 后,對象不會被立即釋放此虑,而是注冊到 autoreleasepool 中甚纲,經(jīng)過一段時間后 pool結(jié)束,此時調(diào)用release方法朦前,對象被釋放介杆。

  • 在MRC的內(nèi)存管理模式下,與對變量的管理相關(guān)的方法有:retain, release 和 autorelease韭寸。retain 和 release 方法操作的是引用記數(shù)春哨,當引用記數(shù)為零時,便自動釋放內(nèi)存恩伺。并且可以用 NSAutoreleasePool 對象赴背,對加入自動釋放池(autorelease 調(diào)用)的變量進行管理,當 drain 時回收內(nèi)存晶渠。

ARC

自動內(nèi)存管理機制凰荚,會根據(jù)引用計數(shù)自動監(jiān)視對象的生存周期,實現(xiàn)方式是在編譯時期自動在已有代碼中插入合適的內(nèi)存管理代碼以及在 Runtime 做一些優(yōu)化乱陡。

  • __strong 是默認使用的標識符浇揩。只有還有一個強指針指向某個對象,這個對象就會一直存活憨颠。

  • __weak 聲明這個引用不會保持被引用對象的存活,如果對象沒有強引用了积锅,弱引用會被置為 nil

  • __unsafe_unretained 聲明這個引用不會保持被引用對象的存活爽彤,如果對象沒有強引用了,它不會被置為 nil缚陷。如果它引用的對象被回收掉了适篙,該指針就變成了野指針。

  • __autoreleasing 用于標示使用引用傳值的參數(shù)(id *)箫爷,在函數(shù)返回時會被自動釋放掉嚷节。

引用循環(huán)

當兩個對象互相持有對方的強引用聂儒,并且這兩個對象的引用計數(shù)都不是0的時候,便造成了引用循環(huán)硫痰。

要想破除引用循環(huán)衩婚,可以從以下幾點入手:

  • 注意變量作用域,使用 autorelease 讓編譯器來處理引用
  • 使用弱引用(weak)
  • 當實例變量完成工作后效斑,將其置為nil

Autorelease Pool

Autorelase Pool 提供了一種可以允許你向一個對象延遲發(fā)送release消息的機制非春。當你想放棄一個對象的所有權(quán),同時又不希望這個對象立即被釋放掉(例如在一個方法中返回一個對象時)缓屠,Autorelease Pool 的作用就顯現(xiàn)出來了奇昙。

所謂的延遲發(fā)送release消息指的是,當我們把一個對象標記為autorelease時:

NSString* str = [[[NSString alloc] initWithString:@"hello"] autorelease];

這個對象的 retainCount 會+1敌完,但是并不會發(fā)生 release储耐。當這段語句所處的 autoreleasepool 進行 drain 操作時,所有標記了 autorelease 的對象的 retainCount 會被 -1滨溉。即 release 消息的發(fā)送被延遲到 pool 釋放的時候了弧岳。

在 ARC 環(huán)境下,蘋果引入了 @autoreleasepool 語法业踏,不再需要手動調(diào)用 autorelease 和 drain 等方法禽炬。

Autorelease Pool 的用處

在 ARC 下,我們并不需要手動調(diào)用 autorelease 有關(guān)的方法勤家,甚至可以完全不知道 autorelease 的存在腹尖,就可以正確管理好內(nèi)存。因為 Cocoa Touch 的 Runloop 中伐脖,每個 runloop circle 中系統(tǒng)都自動加入了 Autorelease Pool 的創(chuàng)建和釋放热幔。

當我們需要創(chuàng)建和銷毀大量的對象時,使用手動創(chuàng)建的 autoreleasepool 可以有效的避免內(nèi)存峰值的出現(xiàn)讼庇。因為如果不手動創(chuàng)建的話绎巨,外層系統(tǒng)創(chuàng)建的 pool 會在整個 runloop circle 結(jié)束之后才進行 drain,手動創(chuàng)建的話蠕啄,會在 block 結(jié)束之后就進行 drain 操作场勤。

例子

for (int i = 0; i < 100000000; i++)
{
    @autoreleasepool
    {
        NSString* string = @"ab c";
        NSArray* array = [string componentsSeparatedByString:string];
    }
}

Autorelease Pool 進行 Drain 的時機

如上面所說,系統(tǒng)在 runloop 中創(chuàng)建的 autoreleaspool 會在 runloop 一個 event 結(jié)束時進行釋放操作歼跟。我們手動創(chuàng)建的 autoreleasepool 會在 block 執(zhí)行完成之后進行 drain 操作和媳。需要注意的是:

  1. 當 block 以異常(exception)結(jié)束時,pool 不會被 drain
  2. Pool 的 drain 操作會把所有標記為 autorelease 的對象的引用計數(shù)減一哈街,但是并不意味著這個對象一定會被釋放掉留瞳,我們可以在 autorelease pool 中手動 retain 對象,以延長它的生命周期(在 MRC 中)骚秦。
  3. Pool drain的時機(Runloop一次周期的休眠前)

Autorelease Pool 與函數(shù)返回值

如果一個函數(shù)的返回值是指向一個對象的指針她倘,那么這個對象肯定不能在函數(shù)返回之前進行 release柴信,這樣調(diào)用者在調(diào)用這個函數(shù)時得到的就是野指針了奈应,在函數(shù)返回之后也不能立刻就 release患雇,因為我們不知道調(diào)用者是
不是 retain 了這個對象个少,如果我們直接 release 了,可能導致后面在使用這個對象時它已經(jīng)成為 nil 了靶溜。

為了解決這個糾結(jié)的問題开瞭, Objective-C 中對對象指針的返回值進行了區(qū)分,一種叫做 retained return value罩息,另一種叫做 unretained return value嗤详。前者表示調(diào)用者擁有這個返回值,后者表示調(diào)用者不擁有這個返回值瓷炮,按照“誰擁有誰釋放”的原則葱色,對于前者調(diào)用者是要負責釋放的,對于后者就不需要了娘香。

  • MRC
    在 MRC 中我們需要關(guān)注這兩種函數(shù)返回類型的區(qū)別苍狰,否則可能會導致內(nèi)存泄露。

對于 retained return value烘绽,需要負責釋放

假設我們有一個 property 定義如下:

@property (nonatomic, retain) NSObject *property;

在對其賦值的時候淋昭,我們應該使用:

self.property = [[[NSObject alloc] init] autorelease];

然后在 dealloc 方法中加入:

[_property release];
_property = nil;

這樣內(nèi)存的情況大體是這樣的:

  1. init 把 retain count 增加到 1
  2. 賦值給 self.property ,把 retain count 增加到 2
  3. 當 runloop circle 結(jié)束時安接,autorelease pool 執(zhí)行 drain翔忽,把 retain count 減為 1
  4. 當整個對象執(zhí)行 dealloc 時, release 把 retain count 減為 0盏檐,對象被釋放

可以看到?jīng)]有內(nèi)存泄露發(fā)生歇式。


對于 unretained return value,不需要負責釋放

當我們調(diào)用非 alloc胡野,init 系的方法來初始化對象時(通常是工廠方法)材失,我們不需要負責變量的釋放,可以當成普通的臨時變量來使用:

NSString *name = [NSString stringWithFormat:@"%@ %@", firstName, lastName];
self.name = name
// 不需要執(zhí)行 [name release]
  • ARC

在 ARC 中我們完全不需要考慮這兩種返回值類型的區(qū)別硫豆,ARC 會自動加入必要的代碼龙巨,因此我們可以放心大膽地寫:

self.property = [[NSObject alloc] init];
self.name = [NSString stringWithFormat:@"%@ %@", firstName, lastName];

參考:Objective-C 內(nèi)存管理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市够庙,隨后出現(xiàn)的幾起案子恭应,更是在濱河造成了極大的恐慌,老刑警劉巖耘眨,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異境肾,居然都是意外死亡剔难,警方通過查閱死者的電腦和手機胆屿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來偶宫,“玉大人非迹,你說我怎么就攤上這事〈壳鳎” “怎么了憎兽?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吵冒。 經(jīng)常有香客問我纯命,道長,這世上最難降的妖魔是什么痹栖? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任亿汞,我火速辦了婚禮,結(jié)果婚禮上揪阿,老公的妹妹穿的比我還像新娘疗我。我一直安慰自己,他們只是感情好南捂,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布吴裤。 她就那樣靜靜地躺著,像睡著了一般溺健。 火紅的嫁衣襯著肌膚如雪麦牺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天矿瘦,我揣著相機與錄音枕面,去河邊找鬼。 笑死缚去,一個胖子當著我的面吹牛潮秘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播易结,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼枕荞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了搞动?” 一聲冷哼從身側(cè)響起躏精,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鹦肿,沒想到半個月后矗烛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡箩溃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年瞭吃,在試婚紗的時候發(fā)現(xiàn)自己被綠了碌嘀。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡歪架,死狀恐怖股冗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情和蚪,我是刑警寧澤止状,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站攒霹,受9級特大地震影響怯疤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜剔蹋,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一旅薄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泣崩,春花似錦少梁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至买优,卻和暖如春妨马,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背杀赢。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工烘跺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人脂崔。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓滤淳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親砌左。 傳聞我的和親對象是個殘疾皇子脖咐,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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