iOS復(fù)習(xí)筆記:內(nèi)存管理之引用計(jì)數(shù)

現(xiàn)在我們使用Objective-C編寫iOS和Mac OS App的時(shí)候都是使用的是ARC來(lái)進(jìn)行內(nèi)存管理的。用一句話來(lái)總結(jié)ARC的功能的話窃这,就是ARC使用編譯器來(lái)代替程序員做內(nèi)存管理的工作轿偎。雖然編譯器幫我們做了內(nèi)存管理工作分扎,但是我們應(yīng)該弄清楚編譯器幫我們做了哪些工作猖闪,哪些工作是編譯器無(wú)法做的,還是需要我們程序員來(lái)完成的暖哨。

OC中對(duì)象的內(nèi)存管理是基于引用計(jì)數(shù)的內(nèi)存管理:(Reference Counted Memory Management)

我們可以想象一下我們?cè)谙到y(tǒng)中使用對(duì)象的情形赌朋,在需要的時(shí)候向系統(tǒng)去申請(qǐng)內(nèi)存,不需要的時(shí)候釋放對(duì)象內(nèi)存篇裁,還給系統(tǒng)沛慢。如果長(zhǎng)期霸占著不需要的內(nèi)存而不釋放,那就是刷流氓达布,會(huì)造成系統(tǒng)的內(nèi)存泄露团甲。內(nèi)存泄露的多了,系統(tǒng)有可能會(huì)崩潰往枣。

內(nèi)存管理 == 引用計(jì)數(shù)伐庭,如果我們明白了引用計(jì)數(shù)的原理和使用的規(guī)則,OC的內(nèi)存管理就變的簡(jiǎn)單了分冈。

對(duì)象使用引用計(jì)數(shù)方法使得內(nèi)存管理變的很簡(jiǎn)單。

我們需要弄清楚兩個(gè)問題:1. 引用計(jì)數(shù)的對(duì)象是什么霸株?2. 引用計(jì)數(shù)是如何變化的雕沉?

引用計(jì)數(shù)的對(duì)象是什么:這個(gè)問題很簡(jiǎn)單,在這里我們討論的引用計(jì)數(shù)的對(duì)象就是Objective-C對(duì)象去件。

引用計(jì)數(shù)是如何變化的:要想回答這個(gè)問題坡椒,我們需要先回答另一個(gè)問題,就是有哪些操作會(huì)影響引用計(jì)數(shù)的變化尤溜?

影響引用計(jì)數(shù)的變化的操作倔叼,大致有以下4種:

  1. 創(chuàng)建對(duì)象從而獲得對(duì)象的所有權(quán)(have ownership),引用計(jì)數(shù)+1宫莱, 從0變成1
  2. 持有對(duì)象從而取得對(duì)象的所有權(quán)(take ownership)丈攒,引用計(jì)數(shù)+1
  3. 釋放對(duì)象從而放棄對(duì)象的所有權(quán)(relinguish ownership),引用計(jì)數(shù)-1授霸;
  4. 如果一個(gè)對(duì)象的引用計(jì)數(shù)-1后為0巡验,意味著這個(gè)對(duì)象不存在所有權(quán)關(guān)系,沒有被其它對(duì)象需要的可能碘耳,也就沒有存在的意義显设,系統(tǒng)會(huì)銷毀(destroy)對(duì)象回收對(duì)象分配的內(nèi)存。這個(gè)銷毀操作是系統(tǒng)自動(dòng)進(jìn)行的辛辨。

我們可以發(fā)現(xiàn)一個(gè)共同點(diǎn)捕捂,上面的這些操作都是針對(duì)對(duì)象的所有權(quán)(ownership)進(jìn)行的瑟枫,對(duì)象的所有權(quán)可以看作是操作對(duì)象的憑證或者授權(quán),沒有對(duì)象的所有權(quán)是不能對(duì)該對(duì)象進(jìn)行操作的指攒。

所以要想操作一個(gè)對(duì)象力奋,首先必要要獲得對(duì)象的所有權(quán),獲得所有權(quán)的方法有兩種:

  1. 如果這個(gè)對(duì)象已經(jīng)存在了幽七,我們只需要聲明持有這個(gè)對(duì)象來(lái)獲得對(duì)象的所有權(quán)景殷,同時(shí)對(duì)象的引用計(jì)數(shù)會(huì)+1
  2. 如果對(duì)象不存在,我們需要新創(chuàng)建一個(gè)對(duì)象澡屡,同時(shí)自然會(huì)獲得這個(gè)新建對(duì)象的所有權(quán)猿挚,新建對(duì)象的引用計(jì)數(shù)從0變成1。

對(duì)于一個(gè)我們沒有所有權(quán)的對(duì)象驶鹉,是不能進(jìn)行操作的绩蜻,如果不小心操作了一個(gè)沒有所有權(quán)的對(duì)象,程序有可能會(huì)崩潰室埋。

OC中有相應(yīng)的方法來(lái)進(jìn)行對(duì)象所有權(quán)操作的方法:

對(duì)象所有權(quán)操作描述 OC方法
新建對(duì)象獲得對(duì)象的所有權(quán) alloc/new/copy/mutableCopy
取得已有對(duì)象的所有權(quán) retain
放棄對(duì)象的所有權(quán) release
銷毀對(duì)象(系統(tǒng)自動(dòng)調(diào)用) dealloc

內(nèi)存管理需要遵循的幾個(gè)規(guī)則:

  1. 能夠獲得任何新創(chuàng)建對(duì)象的所有權(quán):使用alloc/new/copy/mutableCopy開頭的方法來(lái)新建對(duì)象办绝,并取得對(duì)象的所有權(quán)。我用四個(gè)字簡(jiǎn)單總結(jié)就是你建你有姚淆。
  2. 能夠取得其它對(duì)象(不是自己創(chuàng)建的)的所有權(quán):可以使用retain方法取得對(duì)象的所有權(quán)來(lái)操作對(duì)象孕蝉。對(duì)于不是以alloc/new/copy/mutableCopy開頭的方法返回得到的對(duì)象,雖然對(duì)象存在腌逢,但是我們沒有該對(duì)象的所有權(quán)降淮,不能對(duì)它進(jìn)行操作。只有通過retain方法取得對(duì)象的所有權(quán)后搏讶,才能進(jìn)行相應(yīng)的操作佳鳖。簡(jiǎn)單總結(jié)就是他建你有。
  3. 及時(shí)放棄不需要的對(duì)象的所有權(quán):當(dāng)一個(gè)對(duì)象不需要時(shí)媒惕,不管是通過新建對(duì)象還是使用retain方法獲得的對(duì)象所有權(quán)系吩,都必須要調(diào)用release方法來(lái)放棄對(duì)該對(duì)象的所有權(quán)。簡(jiǎn)單來(lái)說(shuō)就是沒用快扔妒蔚。
  4. 不能放棄你沒有所有權(quán)的對(duì)象:對(duì)于沒有所有權(quán)的對(duì)象穿挨,不能調(diào)用release方法,一旦調(diào)用了release方法面睛,程序就有可能出錯(cuò)奔潰絮蒿。簡(jiǎn)單來(lái)說(shuō)就是沒有別動(dòng)。

如果想要進(jìn)一步弄清楚OC對(duì)象的內(nèi)存管理規(guī)則叁鉴,必須要清楚幾個(gè)問題和細(xì)節(jié):

  1. 引用一個(gè)對(duì)象和持有一個(gè)對(duì)象的區(qū)別土涝?或者說(shuō)獲得對(duì)象和獲得對(duì)象所有權(quán)的區(qū)別?
    面向?qū)ο蟮闹R(shí)告訴我們幌墓,一個(gè)對(duì)象的引用其實(shí)就是對(duì)象在內(nèi)存中的地址但壮,通過引用我們可以找到我們需要的對(duì)象的位置冀泻。OC中的內(nèi)存管理告訴我們,持有一個(gè)對(duì)象是指我們不僅能夠知道對(duì)象在內(nèi)存中的位置蜡饵,而且我們還擁有對(duì)象的所有權(quán)弹渔,有了獲得了對(duì)象的所有權(quán)后,我們能夠操作這個(gè)對(duì)象溯祸。
    舉個(gè)不太恰當(dāng)?shù)睦樱罕热缯f(shuō)你暗戀一個(gè)小姐姐肢专,同時(shí)你也知道了小姐姐的住址,但是小姐姐并沒有同意和你在一起焦辅,此時(shí)你只是有了小姐姐的地址博杖,然而并不能對(duì)小姐姐做什么;如果你不僅知道了小姐姐的住址筷登,小姐姐也喜歡你同意和你在一起了剃根,此時(shí)你獲得了小姐姐的所有權(quán)(不太恰當(dāng)?shù)恼f(shuō)法),然后你就能對(duì)小姐姐進(jìn)行一番操作了前方。這就是引用一個(gè)對(duì)象和持有一個(gè)對(duì)象的區(qū)別狈醉。

下面使用一些代碼例子來(lái)說(shuō)明內(nèi)存管理和引用計(jì)數(shù):

Example 1: 創(chuàng)建對(duì)象并獲得對(duì)象所有權(quán)

// 使用alloc方法,并獲得對(duì)象所有權(quán)
id obj1 = [[NSObject alloc] init];
// 使用new方法惠险,并獲得對(duì)象所有權(quán)
id obj2 = [NSObject new];

Example 2: 不是自己創(chuàng)建的對(duì)象苗傅,獲得對(duì)象所有權(quán)

// 獲得一個(gè)不是自己創(chuàng)建的對(duì)象
id obj1 = [NSMutableArray array];
// 獲得對(duì)象的引用,但是沒有獲得對(duì)象的所有權(quán)
[obj1 retain];
//獲得了對(duì)象的所有權(quán)

Example 3: 不需要的對(duì)象莺匠,快快放棄對(duì)象的所有權(quán)

//創(chuàng)建對(duì)象并獲得對(duì)象所有權(quán)
id obj = [[NSObject alloc] init];
//獲得對(duì)象的所有權(quán)金吗,可以操作對(duì)象
...
  
[obj release];
// 放棄對(duì)象的所有權(quán),但是變量obj還是指向?qū)ο笕たⅲ磳?duì)象的引用。但是不能訪問對(duì)象和操作對(duì)象旱物。

Example 4: 方法返回對(duì)象時(shí)的所有權(quán)關(guān)系

//假設(shè)allocMyObject和myObject方法存在于MyClass中
- (id)allocMyObject {
  //新建對(duì)象并獲得所有權(quán)
  id obj = [[NSObject alloc] init];
  // 方法取得對(duì)象的所有權(quán)
  return obj;
  //返回對(duì)象遥缕,并轉(zhuǎn)把對(duì)象的所有權(quán)轉(zhuǎn)移到方法調(diào)用者
}

- (id)myObject {
  //新建對(duì)象并獲得所有權(quán)
  id obj = [[NSObject alloc] init];
  [obj autorelease];
  //方法放棄對(duì)象的所有權(quán)
  return obj;
  //返回對(duì)象,方法調(diào)用者無(wú)法取得對(duì)象的所有權(quán)
}

//myObj是MyClass的一個(gè)實(shí)例對(duì)象
//通過alloc開頭的方法創(chuàng)建的對(duì)象宵呛,獲得對(duì)象的所有權(quán)
id obj1 = [myObj allocMyObject];

//獲得對(duì)象的引用单匣,但是沒有取得對(duì)象的所有權(quán)
id obj2 = [myObj myObject];

Example 5: 不能操作沒有所有權(quán)的對(duì)象

// 新建一個(gè)對(duì)象并取得對(duì)象的所有權(quán)
id obj1 = [[NSObject alloc] init];
//具有對(duì)象的所有權(quán)
[obj1 release];
//放棄對(duì)象的所有權(quán),此時(shí)不能操作對(duì)象


[obj1 release];
// 操作了一個(gè)沒有所有權(quán)的對(duì)象宝穗,系統(tǒng)可能崩潰

// 獲得一個(gè)對(duì)象的引用户秤,但沒有取得對(duì)象的所有權(quán)
id obj2 = [myObj myObject];
// 此時(shí)沒有對(duì)象的所有權(quán)

[obj2 release];
// 操作了一個(gè)沒有所有權(quán)的對(duì)象,系統(tǒng)可能崩潰

總結(jié)

可以用一個(gè)狗和繩圈的比喻來(lái)描述對(duì)象引用計(jì)數(shù)的過程變化:狗脖子要有繩圈才能上街逮矛。當(dāng)需要帶狗上街時(shí)鸡号,你就應(yīng)該給它套上一個(gè)繩圈,表明你取得了這個(gè)狗的所有權(quán)须鼎。當(dāng)別人看上了你的狗鲸伴,他也可以給這個(gè)狗加上一個(gè)繩圈府蔗,表明也取得了這個(gè)狗的所有權(quán)。只要狗的脖子上面還有繩圈汞窗,說(shuō)明這個(gè)狗是有人所有的姓赤,或者說(shuō)是有需求的,這時(shí)候這個(gè)狗是跑不掉的仲吏。當(dāng)別人不需要這個(gè)狗時(shí)不铆,就會(huì)解開他擁有的繩圈,表明放棄了對(duì)這個(gè)狗的所有權(quán)裹唆。當(dāng)所有人都解開了繩圈誓斥,放棄對(duì)這個(gè)狗的所有權(quán)時(shí)。狗的脖子上是沒有繩圈的品腹,這時(shí)狗子一看岖食,沒有繩圈套著了,趕緊溜吧舞吭。這里的??就相當(dāng)于一個(gè)對(duì)象泡垃,脖子上的繩圈就是引用計(jì)數(shù)。

引用計(jì)數(shù)的需要遵循的規(guī)則:

  1. 你建你有
  2. 他建你有
  3. 沒用快扔
  4. 沒有別動(dòng)

我們還需要分清楚對(duì)象的引用和對(duì)象的所有權(quán)的區(qū)別羡鸥,想想小姐姐的故事蔑穴。

如果覺得有用可以去GitHub給個(gè)??。

參考:Pro Multithreading and Memory Management for iOS and OS X: With ARC, Grand Central Dispatch and Blocks.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末惧浴,一起剝皮案震驚了整個(gè)濱河市存和,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌衷旅,老刑警劉巖捐腿,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異柿顶,居然都是意外死亡茄袖,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門嘁锯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)宪祥,“玉大人,你說(shuō)我怎么就攤上這事家乘』妊颍” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵仁锯,是天一觀的道長(zhǎng)耀找。 經(jīng)常有香客問我,道長(zhǎng)扑馁,這世上最難降的妖魔是什么涯呻? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任凉驻,我火速辦了婚禮,結(jié)果婚禮上复罐,老公的妹妹穿的比我還像新娘涝登。我一直安慰自己,他們只是感情好效诅,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布胀滚。 她就那樣靜靜地躺著,像睡著了一般乱投。 火紅的嫁衣襯著肌膚如雪咽笼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天戚炫,我揣著相機(jī)與錄音剑刑,去河邊找鬼。 笑死双肤,一個(gè)胖子當(dāng)著我的面吹牛施掏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播茅糜,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼七芭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蔑赘?” 一聲冷哼從身側(cè)響起狸驳,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缩赛,沒想到半個(gè)月后耙箍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡酥馍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年究西,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片物喷。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖遮斥,靈堂內(nèi)的尸體忽然破棺而出峦失,到底是詐尸還是另有隱情,我是刑警寧澤术吗,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布尉辑,位于F島的核電站,受9級(jí)特大地震影響较屿,放射性物質(zhì)發(fā)生泄漏隧魄。R本人自食惡果不足惜卓练,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望购啄。 院中可真熱鬧襟企,春花似錦、人聲如沸狮含。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)几迄。三九已至蔚龙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間映胁,已是汗流浹背木羹。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留解孙,地道東北人坑填。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像妆距,于是被迫代替她去往敵國(guó)和親穷遂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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