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

聽說這本書很好,所以在項目不怎么忙的時候就讀了讀。總結(jié)了點筆記。

859001-7ceabf4418ec5228.png

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

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

思想一:自己生成的對象,自己持有

alloc
new
copy
mutableCopy

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

思想二:非自己生成的對象,自己也能持有

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

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

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

通過autorelease方法,使對象的持有權(quán)轉(zhuǎn)移給了自動釋放池。所以實現(xiàn)了:調(diào)用方拿到了對象,但這個對象還不被調(diào)用方所持有.

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

思想三:不再需要自己持有的對象時釋放對象

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

或者是

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

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

情況一:多次釋放。

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

情況二:對象不被自己持有,就釋放。

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

實現(xiàn)原理

Objective-C對象中保存著引用計數(shù)這一整數(shù)值。
調(diào)用alloc或者retain方法后,引用計數(shù)+1。
調(diào)用release后,引用計數(shù)-1。
引用計數(shù)為0時,調(diào)用dealloc方法廢棄對象。

autorelease

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

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

所有調(diào)用過autorelease方法的對象,在廢棄NSAutoreleasePool對象時,都將調(diào)用release方法(引用計數(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對象都會被生成或廢棄.

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

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

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

解決辦法:

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

每個循環(huán)都會創(chuàng)建個池子,池子也是循環(huán)完了就釋放。所以自動變量也都釋放了。自動變量都是加在離它最近的池子的。

蘋果的實現(xiàn)

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

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

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

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

    void releaseAll()
    {
        //調(diào)用內(nèi)部數(shù)組中對象的 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原理看這個http://blog.leichunfeng.com/blog/2015/05/31/objective-c-autorelease-pool-implementation-principle/

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

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

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

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

__strong:強引用,持有所指向?qū)ο蟮乃袡?quán),無修飾符情況下的默認值。如需強制釋放,可置nil。

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

obj = nil;

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

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

     __weak __typeof(self) weakSelf = self;

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

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

unsafe_unretained:相當(dāng)于assign。直接賦值。引用計數(shù)不變。他會發(fā)生野指針現(xiàn)象。所以不安全。不像weak,當(dāng)指向的對象為空的時候,將指針置為nil。

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

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

assign:直接賦值

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

  @property (nonatomic, assign) NSInteger count;

當(dāng)然也可以修飾ObjC對象,但是不推薦,因為被assign修飾的對象釋放后,指針還是指向釋放前的內(nèi)存,在后續(xù)操作中可能會導(dǎo)致內(nèi)存問題引發(fā)崩潰。

retain

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

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

ARC對應(yīng)寫法

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

copy

一般用來修飾String、Dict、Array等需要保護其封裝性的對象,尤其是在其內(nèi)容可變的情況下,因此會拷貝(深拷貝)一份內(nèi)容給屬性使用,避免可能造成的對源內(nèi)容進行改動。
使用set方法賦值時,實質(zhì)上是會先拷貝新值,再釋放舊值,再設(shè)置新值。
實際上,遵守NSCopying的對象都可以使用copy,當(dāng)然,如果你確定是要共用同一份可變內(nèi)容,你也可以使用strong或retain。

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

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

 @property (weak) id<MyDelegate> delegate;

strong:

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

block的內(nèi)存管理

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

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

block會對內(nèi)部使用的對象進行強引用,因此在使用的時候應(yīng)該確定不會引起循環(huán)引用,當(dāng)然保險的做法就是添加弱引用標記。

   __weak typeof(self) weakSelf = self;

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

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

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

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

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

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

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

用imageNamed方法加載圖片默認會寫到緩存里,autoReleasePool也不能釋放緩存,對此問題需要另外的解決方法

179360B6-0EE5-4DCD-8BBA-9A0C801F2B8D.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,993評論 2 374

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