Objective--C内存管理基础
2016-08-01 15:36:30
- Objective--C增加了垃圾回收機(jī)制,作為初學(xué)者,需要清楚內(nèi)存的管理,什么時候該申請內(nèi)存,什么時候該釋放內(nèi)存,養(yǎng)成良好的編程習(xí)慣,開發(fā)無內(nèi)存泄漏的應(yīng)用程序。對于自己開發(fā)的應(yīng)用程序,自己管理內(nèi)存,當(dāng)程序需要空間,則手動分配空間,當(dāng)空間或其他資源不再需要時,手動回收。
- OC中的alloc+init為開辟空間,OC中的release是釋放空間的一個標(biāo)示。
2.自動釋放池
- 自動釋放池是OC的一種內(nèi)存自動回收機(jī)制,可以將一些臨時變量通過自動釋放池統(tǒng)一回收釋放。
- 每當(dāng)一個對象接收到autorelease消息時,對象就會被放到自動釋放池中,當(dāng)自動釋放池被釋放時,(自動釋放池中的)對象就會接收到一次release消息。
- 當(dāng)一個對象接收autorelease消息時,系統(tǒng)會把對象放到最近的自動釋放池中,當(dāng)需要清空自動釋放池時,你可以向自動釋放池發(fā)送一個release消息。
1 NSautoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 2 [pool addObject:tt]; 3 [pool addObject:ttt]; 4 [pool addObject:c]; 5 [pool drain];
- 通常情況下,一個Cocoa中的命令行工具應(yīng)用程序main函數(shù)中包含一下代碼:
- 簡單來說,MRC下,當(dāng)向一個對象發(fā)送autorelease消息時,對象不會被立即釋放,而是會被稍后釋放,當(dāng)然,稍后并不是指任意時間。
?
#import <Foundation/Foundation.h>@interface XYPoint : NSObject {int x;int y; }@property (nonatomic) int x, y;- (id)initWithX:(int)_x andY:(int)_y;@end #import "XYPoint.h"@implementation XYPoint@synthesize x, y;- (id)initWithX:(int)_x andY:(int)_y {if (self = [super init]){x = _x;y = _y;}return self; }@end #import <Foundation/Foundation.h> #import "XYPoint.h" int main(int argc, const char * argv[]) {@autoreleasepool{XYPoint *p1 = [[XYPoint alloc] initWithX:1 andY:1];NSLog(@"%d, %d", p1.x, p1.y);[p1 autorelease];sleep(10);p1.x = 10;p1.y = 10;NSLog(@"%d, %d", p1.x, p1.y);}return 0; }?
- 如果向p1發(fā)送autorelease消息,則所有的結(jié)果正常輸出,p1不會被立即釋放。而是當(dāng)自動釋放池釋放時,p1才會被銷毀。
- 使用autorelease需要注意:
- 首先發(fā)送過多的autorelease消息,就和你發(fā)送太多的release一樣,當(dāng)清空自動釋放池時,可能會引發(fā)內(nèi)存故障。
- 其次雖然release消息可以被替換 為autorelease,但是出于對系統(tǒng)性能的考慮,可以使用release的地方盡量不使用autorelease,因為自動釋放池所做的工作要比直接使用release多很多。
- 最后,自動釋放池的“延遲釋放機(jī)制”可能會導(dǎo)致無用的內(nèi)存消耗。
- 在創(chuàng)建對象時,如果你沒有使用alloc,則不需要使用release或者autorelease。但是如果你顯示使用了alloc,則你不要忘記使用release或者autorelease。
- 當(dāng)自動釋放池釋放時,自動釋放池中的對象可能會被釋放。這事因為,當(dāng)自動釋放池釋放時,系統(tǒng)會給釋放池中的每一個對象發(fā)送一個release消息,使得對象的的引用計數(shù)器的值減1,如果對象引用計數(shù)器的值減為0了,則系統(tǒng)會向?qū)ο蟀l(fā)送dealloc消息徹底銷毀對象。
3.引用計數(shù)器
- 當(dāng)涉及OC的根類NSObject或其派生類時,我們往往會使用alloc方法申請內(nèi)存,通過向?qū)ο蟀l(fā)送release消息回收內(nèi)存。但是,事情不總是那么簡單。正在運行的應(yīng)用程序可能在多個地方會引用你創(chuàng)建的對象;比如,一個對象可以被存儲在一個數(shù)組中或者在其他地方被一個實例變量所引用。這種情況下,除非你確定每一個引用該對象的使用者,都不再使用該對象了。否則,你就有可能沒辦法去釋放對象所占的內(nèi)存,如果貿(mào)然回收空間還可能產(chǎn)生內(nèi)存泄漏或者二次刪除的問題。此時,就需要我們對內(nèi)存進(jìn)行管理。
- 內(nèi)存管理在OC中,也是很重要的一部分內(nèi)容,在C中,內(nèi)存申請一次就釋放一次,雖然多個指針可以指向同一塊空間,但是釋放時只能通過氣筒一個指針回收這塊空間。但是,在OC中,增加了“引用計數(shù)器機(jī)制”來記錄該對象被引用的此數(shù),對象被引用幾次,就需要釋放幾次。
- 通常,創(chuàng)建一個新對象,對象引用計數(shù)器的值會被置為1,若在其他代碼中引用該對象,則可以向?qū)ο蟀l(fā)送retain消息,retain可以使對象的引用計數(shù)器的值加1,如果,這段代碼中不在需要這個對象,可以給對象發(fā)送一個ralease消息,是對象引用計數(shù)器的值減1.
- Foundation框架中也有增減對象引用計數(shù)器值得操作,例如,把對象加入數(shù)組或者移出數(shù)組都可以對對象的引用計數(shù)器產(chǎn)生影響。
- AClass *anObject = [[AClass alloc] init];//reference count = 1 after alloc
- [anObject retain]; //reference count += 1 after retain //2
- [anObject retain];//reference count -= 1 after retain //1
- [anObject retain];//reference count == 0 then dealloc//0
實際上,有三種方法可以增減對象引用計數(shù)器的值。這也就意味著你釋放一個對象時,應(yīng)該注意一下幾種限制:
- 顯示使用alloc創(chuàng)建一個對象
- 顯示使用copy[WithZone:]或者mutableCopy[WithZone:]拷貝對象
- 顯示使用retain
使用之前的XYPoint類進(jìn)行測試:
1 #import <Foundation/Foundation.h> 2 #import "XYPoint.h" 3 int main(int argc, const char * argv[]) 4 { 5 @autoreleasepool 6 { 7 XYPoint *p1 = [[XYPoint alloc] initWithX:1 andY:1];//使用alloc,對象引用計數(shù)起得值由0增為1 8 NSLog(@"%d, %d", p1.x, p1.y); 9 NSLog(@"%ld", [p1 retainCount]);//1 10 [p1 retain];//使用retain使對象引用計數(shù)器的值加1 11 NSLog(@"%ld", [p1 retainCount]);//2 12 13 [p1 release]; 14 [p1 release]; 15 16 } 17 return 0; 18 }- 一個對象可以接收任意個retain或者release消息,只要你能保證對象的引用計數(shù)器的值大于0。一旦對象引用計數(shù)器的值減為0,則編譯器會給對象發(fā)送一個dealloc消息徹底銷毀對象(即 dealloc 方法會被自動執(zhí)行)。此時如果繼續(xù)向?qū)ο蟀l(fā)送release消息,則可能會導(dǎo)致內(nèi)存錯誤使程序崩潰。
- 我們可以通過向?qū)ο蟀l(fā)送retainCount消息獲得對象引用計數(shù)器的值。這個方法一般很少用,主要是幫助我們更好的理解當(dāng)一個對象alloc,retain,以及release時,引用計數(shù)器是如何變化的。
4.內(nèi)存分配,初始化
(1)而在OC中,分配空間和初始化是兩個不同的方法。分配空間是通過類方法alloc處理,其中一會對所有的實例變量初始化,但是除了從NSObject繼承的指針isa(is-a)之外的實例變量均被置為0(在運行時,isa的值可以標(biāo)識新創(chuàng)建對象的類型)
(2)但是對實例變量而言,它們的初始值應(yīng)該取決于構(gòu)造函數(shù)中的參數(shù),在Obj中,相關(guān)的初始化代碼會被放在一種方法中,它們的名字通常以init開頭。
(3)在OC中,創(chuàng)建對象嚴(yán)格分成了兩步,空間分配和初始化。
- alloc消息發(fā)送給類對象,init消息則發(fā)送給alloc出來的新對象。并且初始化時不可選的。alloc之后必須要跟著init。
- 在OC中,初始化也是用方法來實現(xiàn),并且,初始化方法一般會以init開頭,而不是強(qiáng)制使用init開頭,但強(qiáng)烈推薦初始化方法命名時遵循如下規(guī)則:初始化方法的方法名須以init開頭。
(4)以下是對正確實現(xiàn)初始化方法的約束或者規(guī)范:
- 名字以init開頭
- 方法需返回一個對象,便于之后的使用。
- 方法體內(nèi),執(zhí)行父類的初始化方法。
- 方法體內(nèi)要檢查父類init方法的返回值。
- 方法體內(nèi)要正確的處理初始化中的錯誤,不管是本類的還是繼承而來的,都要考慮到。
(5)對一個消息來講有兩個特殊的標(biāo)識self和super, self是指當(dāng)前的對象,super是指父類。在OC中,沒有this關(guān)鍵字,進(jìn)而取而代之的是self。其實self不是真正的關(guān)鍵字,每個方法中就會有一個隱藏的參數(shù)self,它的值是當(dāng)前的對象。
(6)MRC下,程序中(alloc, copy, retain)必需要與release成對出現(xiàn)。
(7)處理初始化方法中的錯誤。
(8)當(dāng)使用初始化方法初始化一個對象時,有一下三處潛在危險可能導(dǎo)致程序錯誤。
- 方法的參數(shù)。在執(zhí)行[super init]之前,如果方法的參數(shù)無效,那么初始化必須停止。
- 執(zhí)行父類的init方法。在執(zhí)行父類的init方法時,有可能初始化不成功,此時我們應(yīng)當(dāng)放棄當(dāng)前的初始化操作。
- 類中特有實例變量的初始化。執(zhí)行完父類的init,完成對繼承自父類成員的初始化后,接著為特有的成員進(jìn)行初始化,但是一旦資源分配失敗,則初始化的方法需要終止。
5、內(nèi)存回收
?
轉(zhuǎn)載于:https://www.cnblogs.com/songlei0601/p/5726702.html
總結(jié)
以上是生活随笔為你收集整理的Objective--C内存管理基础的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【BZOJ 4103】 [Thu Sum
- 下一篇: 用int还是用Integer?