Objective-C基礎2:記憶體管理基礎,
1.記憶體儲存地區
C、C++裡面。棧區:儲存臨時變數和對象。堆地區:儲存動態指派至。靜態變數儲存區:儲存靜態變數和常量對象。
OC裡面的記憶體儲存地區跟C、C++一樣。
2.為什麼要進行記憶體管理
寫過C、C++程式的都知道,記憶體管理永遠是C++程式的一大痛點,項目當中崩潰全部來自於記憶體相關的操作,尤其是指標操作和記憶體操作,稍不注意就會產生記憶體訪問違規造成程式崩潰。那麼如何進行記憶體管理呢,個人認為有以下幾點原則:盡量用系統提供給我們的封裝對象,不要用原生的,比如用string而不要用char*,用string能避免項目中大部分的字串違規操作;能在棧上分配就不在堆上分配,利用棧的自動釋放功能幫我們釋放;對於複雜的記憶體操作比如多個類或者多個線程引用同一塊記憶體,那就給這塊記憶體加引用計數;記憶體操作一定要判空操作。
我們知道OC中的所有對象都是動態分配的,那麼OC中是如何管理這些對象的呢?答案是利用引用計數來管理。
3.OC中對象記憶體管理
OC中通過引用計數來管理對象的生命週期。
- (instancetype)retain OBJC_ARC_UNAVAILABLE; //引用加1
- (oneway void)release OBJC_ARC_UNAVAILABLE; //引用減1
- (instancetype)autorelease OBJC_ARC_UNAVAILABLE; //自動釋放對象
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE; //返回當前的引用技術
當對象B擁有對象A時,設定對象A時,最好掉用Retain提升引用計數,在dealloc方法裡面減少引用技術。
注意下面代碼的中(void) setA : (A*) pAIn實現
-(void) setA : (A*) pAIn
{
[pAIn retain];
[pA release];
pA = pAIn;
}
先保留PAIn,再釋放PA,然後賦值,這種寫法能有效地避免PAIn和PA指向同一對象這種情況,強烈建議一定要這樣寫。
代碼如下:
@interface A : NSObject- (void) disInfo;@end@implementation A- (void) disInfo{ NSLog(@"I'm A haha!");}@end@interface B : NSObject{@private A* pA;}-(void) setA : (A*) pAIn;-(void) disA;@end@implementation B- (void)dealloc{ [pA release]; [super dealloc];}-(void) setA : (A*) pAIn{ [pAIn retain]; [pA release]; pA = pAIn;}-(void) disA{ [pA disInfo];}@endint main(int argc, const char * argv[]) { A* pA = [A new]; B* pB = [B new]; [pB setA:pA]; [pB disA]; [pB release]; [pA release]; return 0;}
上面的例子中我們需要手動地掉用retain和release方法,也就是說我們需要知道何時要retain,何時需要release,很顯然在商務邏輯很複雜的情況下需要追蹤引用計數造成的問題需要花費比較多的時間。那麼,如何讓對象自動retain和release呢?寫過Windows Com程式的人知道CComPtr這個智能指標類,OC又是如何做的呢?下面揭曉。
4.OC中自動釋放記憶體
Cocoa中的自動釋放池@autoreleasepool和NSAutoreleasepool配合對象的autorelease方法來實現自動釋放記憶體。
1)利用autoreleasepool和autorelease。代碼如下:
@autoreleasepool { A* pA = [A new]; [pA autorelease]; B* pB = [B new]; [pB autorelease]; [pB setA:pA]; [pB disA]; }
上面的代碼跟3中的代碼相比優勢在:在pA和pB建立完成之後直接調用autorelease方法將對象加入自動釋放吃,代碼緊湊,後面再也不用擔心在什麼時候release了。
2)利用NSAutoreleasePool和autorelease。代碼如下:
NSAutoreleasePool* pool = [NSAutoreleasePool new]; A* pA = [A new]; [pA autorelease]; B* pB = [B new]; [pB autorelease]; [pB setA:pA]; [pB disA]; [pool release];
代碼跟跟利用關鍵字@autoreleasepool的代碼差不多。
那麼面對著兩種方式,我們應該選擇哪種呢?個人認為有一下幾種原則:
1)對於對象操作緊湊的代碼用@autoreleasepool,因為他的效率比NSAutoreleasePool高
2)對象A擁有一些例對象並且這些對象保留在NSArray等數組中,用 NSAutoreleasePool,因為只需要在A的dealloc對象當中調用NSAutoreleasePool的release方法,儲存在NSArray的對象就會自動釋放,很顯然@autoreleasepool不具備這樣的能力。