IOS內(nèi)存管理機制

一僚碎、MRC(手動引用計數(shù)):

不像 java 有垃圾回收機制叫惊,Objective-C 繼承于 C ,使用一套基于對象引用計數(shù)的內(nèi)存管理體系來進行內(nèi)存管理。主要使用 retain十减、release 和 autorelease 進行引用計數(shù)操作。

原理:

1)每個對象內(nèi)部都保存了一個與之相關(guān)聯(lián)的整數(shù)哥放,稱為引用計數(shù)器(reference count)

2)每當使用 alloc智嚷、new 或者 copy 創(chuàng)建一個對象時,對象的引用計數(shù)器被設置為 1

3)給對象發(fā)送一條 retain 消息(即調(diào)用 retain 方法)糠悯,可以使引用計數(shù)器值 +1

4)給對象發(fā)送一條 release 消息帮坚,可以使引用計數(shù)器值 -1

5)當一個對象的引用計數(shù)器值為 0 時,那么它將被銷毀互艾,runtime 會自動向該對象發(fā)送一條 dealloc 消息來回收它所占用的內(nèi)存试和。一般會重寫 dealloc 方法,在這里釋放相關(guān)資源纫普。

6)可以給對象發(fā)送 retainCount 消息獲得當前的引用計數(shù)器值灰署。

原則:

1)對象引用計數(shù)只適用于繼承于 NSObject 對象的 Objective-C 對象,一般數(shù)據(jù)類型不需要使用局嘁。

2)誰創(chuàng)建溉箕,誰釋放。如果你通過 alloc悦昵、new 或者 ( mutable )copy 來創(chuàng)建一個對象肴茄,那么你必須調(diào)用 release 或 autorelease 〉福或句話說寡痰,不是你創(chuàng)建的,就不用你去釋放棋凳。

3)一般來說拦坠,除了 alloc、new 或 copy 之外的方法創(chuàng)建的對象都被聲明了 autorelease( autorelease 是延遲釋放內(nèi)存剩岳,不用你自己去手動釋放贞滨,系統(tǒng)會知道在什么時候該去釋放掉它)。

4)誰 retain拍棕,誰 release晓铆。只要你調(diào)用了 retain,無論這個對象是如何生成的绰播,你都要調(diào)用 release骄噪。

所以,使用 MRC 時,聲明 property 時蠢箩,通常使用 retain链蕊、copy 和 assign 三個修飾符事甜。

assign:不進行引用計數(shù),如果用于 Objective-C 對象滔韵,只是簡單進行賦值逻谦,不進行 retain 操作。還可以用于聲明常規(guī)數(shù)據(jù)類型的屬性奏属。

retain:用于 Objective-C object跨跨,使用后原 object 的 retainCount +1,即只是進行指針復制(淺復制)囱皿。

copy:用于 Objective-C object勇婴,使用后,原 object 不變(包括 retainCount)嘱腥,新建一個 object 并且 retainCount 為 1耕渴,即進行深復制(前提是該 object 必須實現(xiàn) NSCoping 協(xié)議)。

如果一個 property 使用 retain 進行聲明齿兔,它的真實的實現(xiàn)應該是這樣:

比如 property 是 obj:

//getter

-(ObjectClass *)obj

{

return obj;

}

//setter

- (void)setObj:(ObjectClass *)obj

{

if(_obj != obj)

{

[_obj release];

_obj = [obj retain];

}

}

現(xiàn)在橱脸,當在 .m 文件中使用 @synthesize obj 時,編譯器會自動按上面的方式實現(xiàn) setter 方法分苇。

二添诉、引入 autorelease pool:

后來出現(xiàn)了 autorelease pool,這個機制的出現(xiàn)是基于一種十分常見但很難處理的場景:

需要在一個方法中創(chuàng)建并返回一個對象医寿,比如:

- (ClassA *)func

{

ClassA *obj = [[ClassA alloc] init];

return obj;

}

那么問題來了栏赴,是創(chuàng)建者釋放還是調(diào)用者釋放新建的對象呢?如果創(chuàng)建者釋放靖秩,怎么知道調(diào)用者什么時候結(jié)束調(diào)用须眷?如果調(diào)用者釋放,又會因為協(xié)同開發(fā)的原因沟突,造成調(diào)用者忘記釋放的情況花颗。

所以 Objective-C 發(fā)明了 autorelease pool 的概念,上面的代碼可以這樣寫:

- (ClassA *)func

{

ClassA *obj = [[][ClassA alloc] init] autorelease];

return obj;

}

autorelease 可以理解為延遲釋放惠拭,向每一個對象發(fā)送 autorelease 后扩劝,系統(tǒng)都會將該對象放入當前的 autorelease pool,當該 autorelease pool 被釋放時求橄,autorelease pool 會向其中的所有 object 發(fā)送 release 消息今野。

在 iPhone 項目中,默認都有一個 autorelease pool罐农,程序開始時創(chuàng)建,程序退出時銷毀催什,按照對 autorelease 的理解涵亏,豈不是所有 autorelease pool 中的對象都要等到程序退出時才 release,這樣跟內(nèi)存泄露有什么區(qū)別?

其實气筋,每個程序中的 autorelease pool 是嵌套結(jié)構(gòu)的拆内,即一個 autorelease pool 內(nèi)可以嵌套其他的 autorelease pool,對于每個 Runloop宠默,系統(tǒng)會隱式創(chuàng)建一個 autorelease pool麸恍,這樣所有的 autorelease pool 就構(gòu)成一個像 callstack 一樣的棧式結(jié)構(gòu),在 Runloop 結(jié)束時搀矫,當前棧頂?shù)?autorelease pool 會被銷毀抹沪,這樣這個 pool 中的每一個 object 會被 release。

什么是 Runloop 呢瓤球?一個 UI 事件融欧、timer call、delegate call 都是一個新的 Runloop卦羡。例如:

- (IBAction)buttonClicked

{

...

}

- (void)applicationWillTerminate:(UIApplication *)application

{

...

}

都是一個新的 Runloop噪馏。

三、ARC(自動引用計數(shù)):

ARC 是 iOS 5.0 提出的绿饵,ARC 還是利用引用計數(shù)機制進行內(nèi)存管理欠肾,只不過開發(fā)者不需要使用 retain 和 release 來對 retainCount 進行顯示的加減,而是由編譯器在編譯的時候自動添加這些代碼拟赊,而且生成合適的 dealloc 方法刺桃。

在 ARC 模式下是不能使用 retain、release 和 autorelease 關(guān)鍵字的要门。這樣在聲明 property 的時候就引入了 strong虏肾、weak 和 unsafe_unretained 修飾。

strong:用于 Objective-C object欢搜,且 object 的 retainCount 會自動 +1封豪,進行淺復制。

weak:用于 Objective-C object炒瘟,且 object 的 retainCount 不會自動 +1吹埠,如果 object 釋放,weak 指針會自動指向 nil疮装。

unsafe_unretained:用于 Objective-C缘琅,object 的 retainCount 不會自動 +1,且 object 釋放后廓推,unsafe_unretained 指針也不會指向 nil刷袍,會成為野指針。

比如:

情況一樊展,

@property (nonatomic, strong) NSDictionary *firstDict;

@property (nonatomic, strong) NSDictionary *secondDict;

.

self.firstDict = @{@"key":@"value"};

self.secondDict = self.firstDict;

self.firstDict = nil;

NSLog(@"second dict = %@", self.secondDict);

結(jié)果:second dict = @{@"key":@"value"}

情況二呻纹,

@property (nonatomic, strong) NSDictionary *firstDict;

@property (nonatomic, weak) NSDictionary *secondDict;

.

self.firstDict = @{@"key":@"value"};

self.secondDict = self.firstDict;

self.firstDict = nil;

NSLog(@"second dict = %@", self.secondDict);

結(jié)果:second dict = null

情況三堆生,

@property (nonatomic, strong) NSDictionary *firstDict;

@property (nonatomic, unsafe_unretained) NSDictionary *secondDict;

.

self.firstDict = @{@"key":@"value"};

self.secondDict = self.firstDict;

self.firstDict = nil;

NSLog(@"second dict = %@", self.secondDict);

結(jié)果:會 crash

注:strong、weak 和 unsafe_unretained 是 property 的修飾符雷酪。

變量的修飾符對應為:

__strong淑仆、__weak__unsafe_unretained__autoreleasing哥力,它們被稱為 lifetime 修飾符蔗怠。

__strong:變量默認是__strong,只要對象還有強引用,該對象就“活著”吩跋。

__weak:只是簡單引用寞射,weak 對象將被設置為 nil,當對象沒有任何強引用的時候钞澳。

__unsafe_unretained:只是簡單引用怠惶,但是不設置為 nil,當對象沒有任何強引用的時候轧粟。__unsafe_unretained 對象將會產(chǎn)生野指針策治。

__autoreleasing:用于標識 id* 的引用參數(shù),或者需要自動釋放的返回對象兰吟。

lifetime 修飾符的使用:

正確格式:

類名* 修飾符 變量名

使用時注意事項:

1)使用 __weak 問題

NSString * __weak string = [[NSString alloc]? ? initWithFormat:@"First Name: %@", [self firstName]];

NSLog(@"string: %@", string);

這樣盡管 string 在初始化后被使用通惫,但是,在賦值的時候沒有強引用混蔼,因此它將立即被銷毀履腋。

2)循環(huán)引用問題

當涉及到 parent-to-child 與 child-to-parent 引用時,通常 parent-to-child 是 strong惭嚣,child-to-parent 是 weak 來避免循環(huán)引用遵湖。

在類中是用 block,避免類與 block 循環(huán)引用晚吞,通常新建一個 __weak 變量指向該類的實例變量延旧,從而在 block 中使用該實例。

3)在 ARC 模式下槽地,不能使用 NSAutoreleasePool 來管理 autorelease pools迁沫,而是使用 @autoreleasepool 代替它。

@autoreleasepool 比 NSAutoreleasePool 更有效率捌蚊,進入時集畅,pool 被 push,正常退出時 pool 會被 poped 出來缅糟。如果代碼異常退出挺智,pool 不會 pop 出來。

4)開發(fā)者可以在編譯選項中指定每個文件是否使用 ARC 模式窗宦,即使用編譯 flag:-fobjc-arc 與 -fno-objc-arc 逃贝。

5)ARC 只對 Objective-C 有效谣辞,在 ARC 模式下迫摔,編譯器是不會自動管理 Core Foundation 中對象 和 malloc( ) 對象的生命周期的沐扳,所以開發(fā)者必須使用 CFRetain 和 CFRelease 來手動管理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末句占,一起剝皮案震驚了整個濱河市沪摄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纱烘,老刑警劉巖杨拐,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異擂啥,居然都是意外死亡哄陶,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門哺壶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屋吨,“玉大人,你說我怎么就攤上這事山宾≈寥牛” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵资锰,是天一觀的道長敢课。 經(jīng)常有香客問我,道長绷杜,這世上最難降的妖魔是什么直秆? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮鞭盟,結(jié)果婚禮上圾结,老公的妹妹穿的比我還像新娘。我一直安慰自己懊缺,他們只是感情好疫稿,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鹃两,像睡著了一般遗座。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上俊扳,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天途蒋,我揣著相機與錄音,去河邊找鬼馋记。 笑死号坡,一個胖子當著我的面吹牛懊烤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播宽堆,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼腌紧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了畜隶?” 一聲冷哼從身側(cè)響起壁肋,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎籽慢,沒想到半個月后浸遗,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡箱亿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年跛锌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片届惋。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡髓帽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盼樟,到底是詐尸還是另有隱情氢卡,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布晨缴,位于F島的核電站译秦,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏击碗。R本人自食惡果不足惜筑悴,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望稍途。 院中可真熱鬧阁吝,春花似錦、人聲如沸械拍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坷虑。三九已至甲馋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間迄损,已是汗流浹背定躏。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人痊远。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓垮抗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親碧聪。 傳聞我的和親對象是個殘疾皇子冒版,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

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