標籤:objective-c ios ios開發 記憶體管理
記憶體管理:
- 確保在需要的時候分配記憶體,在程式運行結束時釋放佔用的記憶體
- 如果只分配記憶體而不釋放記憶體,則會發生記憶體流失(leak memory),程式的記憶體佔用量不斷增加,最終會被耗盡並導致程式崩潰。
- 不要使用任何剛釋放的記憶體,否則可能誤用陳舊的資料,如果記憶體已經載入了其他資料,將會破壞這些新資料。
9.1 對象生命週期
對象的生命週期:
- 誕生:通過alloc或new方法實現
- 生存:接受訊息並執行操作
- 交友:通過複合以及向方法傳遞函數
- 死去:被釋放掉
9.11 引用計數
關於引用計數的操作:
- 增加對象的保留計數器的值:發送retain 。方法:
-(id) retain //傳回值為id類型
[[Car retain] setTire: tire atIndex:2];//car對象保留計數器的值+1並執行setTire的操作
- 減少對象的保留計數器的值:發送release。方法:
(oneway void)release;
- 獲得保留計數器當前的值:retainCount。方法:
-(NSUInteger) retainCount;//格式化方法:%ld
- 對象的保留計數器歸0時,系統會自動向對象發送dealloc訊息。
- 可以在自己的對象中重寫dealloc方法,這樣可以釋放掉已經分配的全部相關資源,不能直接調用dealloc方法。
9.12 對象所有權
- 某個實體擁有一個對象時,該實體就要負責將其擁有的對象進行清理
- 如果一個對象內有指向其他對象的執行個體變數,則稱該對象擁有這些對象
- 如果一個函數建立了一個對象,則稱該函數擁有這個對象
main(){Car *car = [Car new];Engine *engine = [Engine new];//engine擁有engine對象[car setEngine: engine];//car擁有engine對象}
注意:main()和Car類都擁有engine對象,如何釋放?
- 讓Car類在setEngie方法中保留engine對象,main()負責釋放engine對象,Car類完成任務時再釋放engine對象
9.13 存取方法中的保留和釋放
-(void)setEngine: (Engine *)newEngine{ [newEngine retain]; //先保留新的對象 [engine release]; //釋放舊的對象 engine = newEngine;
9.14 自動釋放
-(NSString *) description{ NSString *description = [[NSString alloc] initWithFormat:@"I am %d years old", 4]; return (destription);}main{ NSString *desc = [someObject description]; NSLog(@"@",desc); [desc release];}
注意:在description方法中建立的字串對象如何釋放?
9.15 所有對象放入池中
-(id)autorelease;//返回接受這條訊息的對象
- 當給一個對象發送autorelease訊息時,是將對象添加到了自動釋放池中,當自動釋放池被銷毀時,會像該池中的所有對象發送release訊息。
-(NSString *) description{ NSString *description = [[NSString alloc] initWithFormat:@"I am %d years old", 4]; return ([destription autorelease]);//對象暫時放入池中,等調用NSLog代碼結束後,自動釋放池會被自動銷毀}main(){ NSLog(@"@",[someObject description]);}
9.16 自動釋放池銷毀時間 略9.17 自動釋放池的工作流程
int main (){ NSAutoreleasePool *pool; pool = [[NSAutoreleasePool alloc] init]; RetainTracker *tracker; tracker = [RetainTracker new]; // count: 1 [tracker retain]; // count: 2 [tracker autorelease]; // count: still 2,自動釋放池有一個引用指向了該對象,當自動釋放池被銷毀時,將給tracker對象發送一條release訊息。保留計數器的值仍然大於0,仍處於活動狀態。 [tracker release]; // count: 1 NSLog (@"releasing pool"); [pool release]; //dealloc方法調用 return (0);}
9.2 Cocoa的記憶體管理規則
- 使用new,alloc,或copy方法建立對象時,該對象的保留計數器為1。
- 如果通過其他對象獲得一個對象時,假設該對象的保留計數器值為1,而且一景被設定為自動釋放,那麼不需要執行任何操作確保該對象得到清理。
- 如果打算一段時間擁有對象,則需要保留它並確保在操作完成時釋放它。
- 如果保留了某個對象,就需要最終釋放或自動釋放該對象。必須保持retain方法和release方法的使用次數相等。
注意:當擁有一個對象的時候,需要弄清楚:怎樣獲得對象的?打算擁有多長時間?
9.21 臨時對象
並未打算長期擁有對象的情況下:臨時對象
- 如果是用new,alloc,copy方法獲得的這個對象,就需要安排好該對象的記憶體釋放
- new,alloc,copy以外的方法獲得對象,則可以假設該對象被返回時保留計數器的值是1而且被設定為自動釋放。
9.22 擁有對象
在多段代碼中一直擁有某個對象,將她們加入到諸如NSArray或NSDictionary集合中
- 如果使用了new,alloc,copy方法獲得了一個對象,只需要在擁有該對象的dealloc方法中釋放它即可。
-(void)dostuff{ flonkArray = [NSMutableArray new]; //count:1}
-(void) dealloc{ [flonkArray release]; //count:0 [super dealloc];}
- 如果使用new,alloc,copy以外的方法獲得了一個對象,需要保留該對象,因為在事件迴圈結束後或自動釋放池被銷毀時,該對象會收到一條release訊息。
-(void)dostuff{ flonkArray = [NSMutableArray arrayWithCapacity:17]; //count:1,autoreleased}
-(void) dealloc{ [flonkArray release]; //count:0 [super dealloc];}
注意:自動釋放池被清理的時間是完全確定的:在代碼中你自己手動銷毀;使用AppKit時在事件迴圈結束時結束。
9.23 記憶體回收:自動記憶體管理機制
記憶體回收行程定期檢查變數和對象並且跟蹤它們的指標,發現沒有任何變數指向某個對象時,就將該對象視為應該丟棄的垃圾。
如果執行個體變數指向某個對象,一定要將該執行個體賦值為nil,取消對該對象的引用並告知記憶體回收行程該對象可以清理了。
注意:
- 記憶體回收只支援OSX開發,無法用在ios應用程式上。蘋果公司不建議在自己的代碼中使用autorelease方法,也不要使用會返回自動釋放對象的一些便利的方法:
stringWith:
- 記憶體回收行程在運行時工作,通過返回的代碼定期檢查對象
9.24 自動引用計數
自動引用計數(automatic refrence counting,ARC):系統追蹤對象並決定哪一個仍會使用,哪一個不會再用到。
ARC在編譯時間進行工作的,在代碼中插入了retain和release語句。
有效範圍:可保留的對象指標
- 代碼塊指標
- Objective-C指標
- 通過attribute((NSObject))類型定義的指標
使用ARC滿足的條件:
- 能夠確定哪些對象需要記憶體管理
- 能夠表面如何管理對象
- 有可行的辦法傳遞對象的所有權
A引用了B,B的引用計數器+1,強指標;被引用的+1
A釋放了B,B的引用計數器-1;被釋放的-1
歸零弱引用:zeroing weak reference 若引用的對象被釋放的情況下,若引用會被設定為0
聲明歸零弱引用:
_weak NSString *mystring
@property (weak) NSString *myString
注意:記憶體管理的關鍵字和特性是不能一起用的
記憶體回收機制禁用的前提下才能使用ARC
轉換之前確保代碼符合ARC的需求
一旦轉換成ARC版本,就不可以再恢複了
9.3 異常
NSException類來表示異常
- 拋出異常(提出異常):處理異常的行為,通知異常的行為
- 捕捉異常:處理被拋出的異常的行為
如果一個異常被拋出但是沒有被捕捉到,程式會在異常斷點處停止運動並通知有這個異常。
9.31 與異常有關的關鍵字
- @try:定義用來測試的代碼塊決定是否拋出異常
- @catch:定義用來處理已拋出異常的代碼塊
- @finally:無論是否有拋出異常都會執行代碼塊
- @throw:拋出異常
9.32 捕捉不同類型的異常
- 多個@catch代碼塊,處理代碼應該按照從具體到抽象的順序排序
9.33 拋出異常
- 當程式檢測到了異常,就必須向處理它的代碼塊報告這個異常
- 使用@“throw異常名”
- 向某個NSException對象發送raise訊息
NSException *theException = [NSException exceptionWithName: ];@throw theException; //拋出異常,可以用在其他對象上[theException rasie];//拋出異常,raise只對NSException對象有效
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
[學習筆記—Objective-C]《Objective-C-基礎教程 第2版》第九章 記憶體管理