《Objective-C高級(jí)編程:iOS與OS X多線程和內(nèi)存管理》之 自動(dòng)引用計(jì)數(shù)

聽說這本書很好荣暮,所以在項(xiàng)目不怎么忙的時(shí)候就讀了讀≌肿ぃ總結(jié)了點(diǎn)筆記穗酥。

859001-7ceabf4418ec5228.png

手動(dòng)內(nèi)存管理MRC

  • 內(nèi)存管理的思想
    思想一:自己生成的對(duì)象,自己持有惠遏。
    思想二:非自己生成的對(duì)象砾跃,自己也能持有。
    思想三:不再需要自己持有的對(duì)象時(shí)釋放對(duì)象节吮。
    思想四:非自己持有的對(duì)象無法釋放抽高。
    從上面的思想來看,我們對(duì)對(duì)象的操作可以分為三種:生成透绩,持有翘骂,釋放,再加上廢棄帚豪,一共有四種碳竟。它們所對(duì)應(yīng)的Objective-C的方法和引用計(jì)數(shù)的變化是:
436A54CE-6351-4973-8161-120549A416CE.png

思想一:自己生成的對(duì)象,自己持有

alloc
new
copy
mutableCopy

如 id obj = [[NSObject alloc] init];//持有新生成的對(duì)象

思想二:非自己生成的對(duì)象狸臣,自己也能持有

id obj = [NSMutableArray array];//非自己生成并持有的對(duì)象
[obj retain];//持有新生成的對(duì)象

來看看[NSMutableArray array]是怎么個(gè)實(shí)現(xiàn)法莹桅,和自己生成的對(duì)象有什么區(qū)別:

- (id)object
{
    id obj = [[NSObject alloc] init];//持有新生成的對(duì)象
    [obj autorelease];//自動(dòng)釋放
    return obj;
}

通過autorelease方法,使對(duì)象的持有權(quán)轉(zhuǎn)移給了自動(dòng)釋放池烛亦。所以實(shí)現(xiàn)了:調(diào)用方拿到了對(duì)象统翩,但這個(gè)對(duì)象還不被調(diào)用方所持有.

E33F7638-D143-45FA-BEBA-FBCCC0264E93.png

思想三:不再需要自己持有的對(duì)象時(shí)釋放對(duì)象

id obj = [[NSObject alloc] init];//持有新生成的對(duì)象
[obj release];//事情做完了,釋放該對(duì)象

或者是

id obj = [NSMutableArray array];//非自己生成并持有的對(duì)象
[obj retain];//持有新生成的對(duì)象
[obj release];//事情做完了此洲,釋放該對(duì)象

思想四:無法釋放非自己持有的對(duì)象

情況一:多次釋放厂汗。

id obj = [[NSObject alloc] init];//持有新生成的對(duì)象
[obj release];//釋放該對(duì)象,不再持有了
[obj release];//釋放已經(jīng)廢棄了的對(duì)象呜师,崩潰

情況二:對(duì)象不被自己持有娶桦,就釋放。

id obj = [NSMutableArray array];//非自己生成并持有的對(duì)象
[obj release];//釋放了非自己持有的對(duì)象

實(shí)現(xiàn)原理

Objective-C對(duì)象中保存著引用計(jì)數(shù)這一整數(shù)值汁汗。
調(diào)用alloc或者retain方法后衷畦,引用計(jì)數(shù)+1。
調(diào)用release后知牌,引用計(jì)數(shù)-1祈争。
引用計(jì)數(shù)為0時(shí),調(diào)用dealloc方法廢棄對(duì)象角寸。

autorelease

autorelease的具體使用方法如下:
生成并持有NSAutoreleasePool對(duì)象菩混。
調(diào)用已分配對(duì)象的autorelease方法忿墅。
廢棄NSAutoreleasePool對(duì)象。

8DB271FD-F954-4672-89FA-3434A8F02A4E.png

所有調(diào)用過autorelease方法的對(duì)象沮峡,在廢棄NSAutoreleasePool對(duì)象時(shí)疚脐,都將調(diào)用release方法(引用計(jì)數(shù)-1)

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];//相當(dāng)于obj調(diào)用release方法

NSRunLoop在每次循環(huán)過程中,NSAutoreleasePool對(duì)象都會(huì)被生成或廢棄.

096923F9-158F-4836-846C-DB293855CA83.png

這樣會(huì)有個(gè)問題如果是生成大量的autorelease對(duì)象邢疙,只要不廢棄NSAutoreleasePool的話對(duì)象就不能釋放棍弄,所以會(huì)產(chǎn)生內(nèi)存不足的問題。由上圖NSRunLoop是在應(yīng)用程序主線程處理完了才會(huì)廢棄Pool疟游。所以如果在for循環(huán)里創(chuàng)建好多的局部對(duì)象呼畸,他們得不到及時(shí)的釋放,就會(huì)使得程序因?yàn)閮?nèi)存不足奔潰颁虐。

for (int i = 0; i < 10000; i++)
{
    圖像文件讀入到data對(duì)象蛮原,
   data生成UIimage對(duì)象,
   改變對(duì)象的尺寸生成一個(gè)新的UIimage對(duì)象
}

解決辦法:

for (int i = 0; i < 10000; i++)
{
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    圖像文件讀入到data對(duì)象聪廉,
    data生成UIimage對(duì)象,
    改變對(duì)象的尺寸生成一個(gè)新的UIimage對(duì)象
  [pool drain];//相當(dāng)于obj調(diào)用release方法
  
}

每個(gè)循環(huán)都會(huì)創(chuàng)建個(gè)池子故慈,池子也是循環(huán)完了就釋放板熊。所以自動(dòng)變量也都釋放了。自動(dòng)變量都是加在離它最近的池子的察绷。

蘋果的實(shí)現(xiàn)

class AutoreleasePoolPage
{
    static inline void *push()
    {
        //生成或者持有 NSAutoreleasePool 類對(duì)象
    }

    static inline void pop(void *token)
    {
        //廢棄 NSAutoreleasePool 類對(duì)象
        releaseAll();
    }

    static inline id autorelease(id obj)
    {
        //相當(dāng)于 NSAutoreleasePool 類的 addObject 類方法
        AutoreleasePoolPage *page = 取得正在使用的 AutoreleasePoolPage 實(shí)例;
       autoreleaesPoolPage->add(obj)
    }

    id *add(id obj)
    {   
        //將對(duì)象追加到內(nèi)部數(shù)組中
    }

    void releaseAll()
    {
        //調(diào)用內(nèi)部數(shù)組中對(duì)象的 release 方法
    }
};

//壓棧
void *objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}

//出棧
void objc_autoreleasePoolPop(void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}

id *objc_autorelease(id obj)
{
   return  AutoreleasePoolPage::autorelease(obj);
}

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// 等同于 objc_autoreleasePoolPush

id obj = [[NSObject alloc] init];
[obj autorelease];
// 等同于 objc_autorelease(obj)

[pool drain];
// 等同于 objc_autoreleasePoolPop(pool)

想要了解更多autoreleasepool原理看這個(gè)http://blog.leichunfeng.com/blog/2015/05/31/objective-c-autorelease-pool-implementation-principle/

ARC下的內(nèi)存管理

在ARC機(jī)制下干签,編譯器就可以自動(dòng)進(jìn)行內(nèi)存管理,減少了開發(fā)的工作量拆撼。但我們有時(shí)仍需要四種所有權(quán)修飾符來配合ARC來進(jìn)行內(nèi)存管理容劳。

四種所有權(quán)修飾符

ARC提供四種修飾符,分別是strong, weak, autoreleasing, unsafe_unretained

__strong:強(qiáng)引用闸度,持有所指向?qū)ο蟮乃袡?quán)竭贩,無修飾符情況下的默認(rèn)值。如需強(qiáng)制釋放莺禁,可置nil留量。

NSObject *obj = [[NSObject alloc]init];
  他們是等價(jià)的
NSObject *__strong obj = [[NSObject alloc]init];

obj = nil;

__weak:弱引用哟冬,不持有所指向?qū)ο蟮乃袡?quán)楼熄,引用指向的對(duì)象內(nèi)存被回收之后,引用本身會(huì)置nil浩峡,避免野指針可岂。

比如避免循環(huán)引用的弱引用聲明:

     __weak __typeof(self) weakSelf = self;

_autoreleasing:將對(duì)象賦值給附有 _ autoreleasing 修飾符的變量等同于ARC 無效時(shí)調(diào)用對(duì)象的autorelease方法

@autoreleasepool {
    id __autoreleasing obj = [[NSObject alloc] init];
}

unsafe_unretained:相當(dāng)于assign。直接賦值翰灾。引用計(jì)數(shù)不變缕粹。他會(huì)發(fā)生野指針現(xiàn)象稚茅。所以不安全。不像weak致开,當(dāng)指向的對(duì)象為空的時(shí)候峰锁,將指針置為nil。

屬性的內(nèi)存管理

ObjC2.0引入了@property双戳,提供成員變量訪問方法虹蒋、權(quán)限、環(huán)境飒货、內(nèi)存管理類型的聲明魄衅,下面主要說明ARC中屬性的內(nèi)存管理
屬性的參數(shù)分為三類,基本數(shù)據(jù)類型默認(rèn)為(atomic,readwrite,assign)塘辅,對(duì)象類型默認(rèn)為(atomic,readwrite,strong)晃虫,其中第三個(gè)參數(shù)就是該屬性的內(nèi)存管理方式修飾,修飾詞可以是以下之一:

assign:直接賦值

assign一般用來修飾基本數(shù)據(jù)類型

  @property (nonatomic, assign) NSInteger count;

當(dāng)然也可以修飾ObjC對(duì)象扣墩,但是不推薦哲银,因?yàn)楸籥ssign修飾的對(duì)象釋放后,指針還是指向釋放前的內(nèi)存呻惕,在后續(xù)操作中可能會(huì)導(dǎo)致內(nèi)存問題引發(fā)崩潰荆责。

retain

retain和strong一樣,都用來修飾ObjC對(duì)象
使用set方法賦值時(shí)亚脆,實(shí)質(zhì)上是會(huì)先保留新值做院,再釋放舊值,再設(shè)置新值濒持,避免新舊值一樣時(shí)導(dǎo)致對(duì)象被釋放的的問題
MRC寫法如下

 - (void)setCount:(NSObject *)count {
         [count retain];
         [_count release];
         _count = count;
     }

ARC對(duì)應(yīng)寫法

- (void)setCount:(NSObject *)count {
        _count = count;
    }

copy

一般用來修飾String键耕、Dict、Array等需要保護(hù)其封裝性的對(duì)象柑营,尤其是在其內(nèi)容可變的情況下屈雄,因此會(huì)拷貝(深拷貝)一份內(nèi)容給屬性使用,避免可能造成的對(duì)源內(nèi)容進(jìn)行改動(dòng)官套。
使用set方法賦值時(shí)棚亩,實(shí)質(zhì)上是會(huì)先拷貝新值,再釋放舊值虏杰,再設(shè)置新值讥蟆。
實(shí)際上,遵守NSCopying的對(duì)象都可以使用copy纺阔,當(dāng)然瘸彤,如果你確定是要共用同一份可變內(nèi)容,你也可以使用strong或retain笛钝。

weak:ARC新引入修飾詞质况,可代替assign愕宋,比assign多增加一個(gè)特性(置nil)

weak和strong一樣用來修飾ObjC對(duì)象。
使用set方法賦值時(shí)结榄,實(shí)質(zhì)上不保留新值中贝,也不釋放舊值,只設(shè)置新值臼朗。

 @property (weak) id<MyDelegate> delegate;

strong:

ARC新引入修飾詞邻寿,可代替retain.ARC一般都寫strong。

block的內(nèi)存管理

OS中使用block必須自己管理內(nèi)存视哑,錯(cuò)誤的內(nèi)存管理將導(dǎo)致循環(huán)引用等內(nèi)存泄漏問題绣否,這里主要說明在ARC下block聲明和使用的時(shí)候需要注意的兩點(diǎn):
1)如果你使用@property去聲明一個(gè)block的時(shí)候,一般使用copy來進(jìn)行修飾(當(dāng)然也可以不寫挡毅,編譯器自動(dòng)進(jìn)行copy操作)蒜撮,盡量不要使用retain。

   @property (nonatomic, copy) void(^block)(NSData * data);

block會(huì)對(duì)內(nèi)部使用的對(duì)象進(jìn)行強(qiáng)引用跪呈,因此在使用的時(shí)候應(yīng)該確定不會(huì)引起循環(huán)引用段磨,當(dāng)然保險(xiǎn)的做法就是添加弱引用標(biāo)記。

   __weak typeof(self) weakSelf = self;

循環(huán)中對(duì)象占用內(nèi)存大

這個(gè)問題常見于循環(huán)次數(shù)較大耗绿,循環(huán)體生成的對(duì)象占用內(nèi)存較大的情景苹支。

 for (int i = 0; i < 10000; i ++) {
         Person * p = [[Person alloc]init];
     }

該循環(huán)內(nèi)產(chǎn)生大量的臨時(shí)對(duì)象,直至循環(huán)結(jié)束才釋放缭乘,可能導(dǎo)致內(nèi)存泄漏沐序,在循環(huán)中創(chuàng)建自己的autoReleasePool琉用,及時(shí)釋放占用內(nèi)存大的臨時(shí)變量堕绩,減少內(nèi)存占用峰值。

for (int i = 0; i < 10000; i ++) {
         @autoreleasepool {
             Person * p = [[Person alloc]init];
         }
 }

特殊情況
假如有10000張圖片邑时,每張2M左右奴紧,現(xiàn)在需要獲取所有圖片的尺寸,你會(huì)怎么做晶丘?

0EED4362-F987-4DCF-929E-5FB8250C67D4.png

用imageNamed方法加載圖片默認(rèn)會(huì)寫到緩存里黍氮,autoReleasePool也不能釋放緩存,對(duì)此問題需要另外的解決方法

179360B6-0EE5-4DCD-8BBA-9A0C801F2B8D.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末浅浮,一起剝皮案震驚了整個(gè)濱河市沫浆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌滚秩,老刑警劉巖专执,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舶掖,死亡現(xiàn)場(chǎng)離奇詭異镊屎,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門藐不,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人将饺,你說我怎么就攤上這事矢渊。” “怎么了躬审?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵棘街,是天一觀的道長。 經(jīng)常有香客問我盒件,道長蹬碧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任炒刁,我火速辦了婚禮恩沽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘翔始。我一直安慰自己罗心,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布城瞎。 她就那樣靜靜地躺著渤闷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪脖镀。 梳的紋絲不亂的頭發(fā)上飒箭,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音蜒灰,去河邊找鬼弦蹂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛强窖,可吹牛的內(nèi)容都是我干的凸椿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼翅溺,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼脑漫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起咙崎,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤优幸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后褪猛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體网杆,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了跛璧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片严里。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖追城,靈堂內(nèi)的尸體忽然破棺而出刹碾,到底是詐尸還是另有隱情,我是刑警寧澤座柱,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布迷帜,位于F島的核電站,受9級(jí)特大地震影響色洞,放射性物質(zhì)發(fā)生泄漏戏锹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一火诸、第九天 我趴在偏房一處隱蔽的房頂上張望锦针。 院中可真熱鬧,春花似錦置蜀、人聲如沸奈搜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽馋吗。三九已至,卻和暖如春秋秤,著一層夾襖步出監(jiān)牢的瞬間宏粤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來泰國打工灼卢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绍哎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓芥玉,卻偏偏與公主長得像蛇摸,于是被迫代替她去往敵國和親备图。 傳聞我的和親對(duì)象是個(gè)殘疾皇子灿巧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

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