一僚碎、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 來手動管理。