標籤:記憶體管理 objective-c 蘋果開發 ios開發 objective-c文法
本分享是面向有意向從事iOS開發的夥伴以及蘋果產品的發燒友們,或者已經從事了iOS的開發人員,想進一步提升者。如果您對iOS開發有極高的興趣,可以與我一起探討iOS開發,一起學習,共同進步。如果您是零基礎,建議您先翻閱我之前分享的iOS開發分分鐘搞定C語言系列,然後在開始Objective C語言的學習,如果您遇到問題也可以與我探討,另外將無償分享自己整理出來的大概400G iOS學習視頻及學習資料,都是乾貨哦!可以新浪微博私信?關注極客James,期待與您的共同學習和探討!!由於時間有限,每天在工作之餘整理的學習分享,難免有不足之處,也希望各路大神指正!
made by :極客James
一、為什麼要學習記憶體管理
記憶體對於任何硬體及軟體的重要性不言而喻,而作為硬體與使用者交流的中間體軟體,對於記憶體的要求以及如何使用記憶體,如何合理分配記憶體是至關重要的,而Objective C語言是C語言的超集,對於記憶體以及記憶體管理方面也非常重要。在iOS開發中,記憶體泄露會嚴重影響使用者體驗,而蘋果的硬體裝置的記憶體本身記憶體就不大,所以作為iOS開發人員,掌握記憶體管理是非常重要的,不過從Xcode 4.2 之後蘋果公司採用ARC編譯器特性的記憶體管理機制,大大的減少了程式員手動對記憶體的管理,讓程式員把注意點放在產品邏輯上,使得蘋果的軟體產品做的更加出色。雖然現在做iOS開發不採用MRC來手動管理記憶體,但掌握MRC的記憶體管理機制對於提升開發思維以及更深瞭解記憶體管理機制是非常有必要的。
二、多種語言的記憶體管理
(1)java語言:java虛擬機器及記憶體回收機制。
(2)C++語言:相關C++記憶體管理 相對比較複雜,手動管理,程式員的痛點之一。
(3)Android:如果你懂java,就會更容易理解Android系統的記憶體管理機制。與java的記憶體回收機制類似,系統有一個規則來回收記憶體。進行記憶體調度有個閾值,只有低於這個值系統才會按一個列表來關閉使用者不需要的東西。這種記憶體調度管理方式有個弊端,容易造成記憶體泄露,所以很多安卓手機經常會出現卡機及提示記憶體滿的狀況,眾多安卓使用者的痛點。
(4)Objective C 語言:ARC編譯器管理機制,程式員在使用Xcode 4.2以上版本進行軟體開發時,編譯器自動在相應的位置進行記憶體釋放,不需要手動管理。
三、堆和棧
(1)棧(作業系統):
由作業系統自動分配釋放,存放函數的參數值,局部變數的值等。其操作方式類似於資料結構中的棧(先進後出);
(2)堆(作業系統):
一般由程式員分配釋放,若程式員不釋放,程式結束時可能由OS回收,分配方式類似於鏈表。
四、殭屍對象、野指標、null 指標
1.殭屍對象
已經被銷毀的對象(不能再使用的對象)
2.野指標
指向殭屍對象(不可用記憶體)的指標
給野指標發訊息會報EXC_BAD_ACCESS錯誤
3.null 指標
沒有指向儲存空間的指標(裡面存的是nil, 也就是0)
給null 指標發訊息是沒有任何反應的
為了避免野指標錯誤的常見辦法
在對象被銷毀之後, 將指向對象的指標變為空白指標
五、Objective C語言的記憶體管理
所謂記憶體管理, 就是對記憶體進行管理, 涉及的操作有:
(1)分配記憶體 : 比如建立一個對象, 會增加記憶體佔用
(2)清除記憶體 : 比如銷毀一個對象, 能減小記憶體佔用
記憶體管理的管理範圍
(1)任何繼承了NSObject的對象
(2)對其他非物件類型無效(int、char、float、double、struct、enum等 )
只有OC對象才需要進行記憶體管理的根本原因:
OC對象存放於堆裡面
非OC對象一般放在棧裡面(棧記憶體會被系統自動回收)
六、MRC手動管理記憶體
Manual Reference Counting:(MRC)
1.引用計數器
(1)引用計數器表示有多少人正在使用這個對象。
(2)當沒有任何人使用這個對象時, 系統才會回收這個對象, 也就是說當對象的引用計數器為0時,對象佔用的記憶體就會被系統回收。
(3)如果對象的計數器不為0,那麼在整個程式運行過程,它佔用的記憶體就不可能被回收(除非整個程式已經退出 )
(4)任何一個對象, 剛生下來的時候, 引用計數器都為1
(5)當使用alloc、new或者copy(MutableCopy)建立一個對象時,對象的引用計數器預設就是1
2.引用計數器的操作
(1)給對象發送一條retain訊息,可以使引用計數器值+1(retain方法返回對象本身)
(2)給對象發送一條release訊息, 可以使引用計數器值-1
(3)給對象發送retainCount訊息, 可以獲得當前的引用計數器值(retainCount有時候會不準確,建議採用delloc方法來驗證是否完全記憶體釋放)
注意: release並不代表銷毀\回收對象, 僅僅是計數器-1
3.dealloc方法
(1)當一個對象的引用計數器值為0時,這個對象即將被銷毀,其佔用的記憶體被系統回收。
(2)對象即將被銷毀時系統會自動給對象發送一條dealloc訊息 (因此, 從dealloc方法有沒有被調用,就可以判斷出對象是否被銷毀)
dealloc方法的重寫
一般會重寫dealloc方法,在這裡釋放相關資源,dealloc就是對象的遺言
一旦重寫了dealloc方法, 就必須調用[super dealloc],並且放在最後面調用
-(void)delloc{ [super delloc];}
4.使用注意
不能直接調用dealloc方法
一旦對象被回收了, 它佔用的記憶體就不再可用,堅持使用會導致程式崩潰(野指標錯誤).
5.記憶體管理規則
(1)誰建立誰release :
如果你通過alloc、new或[mutable]copy來建立一個對象,那麼你必須調用release或autorelease
誰retain誰release:
(2)只要你調用了retain,就必須調用一次release
總結:
有加就有減
曾經讓對象的計數器+1,就必須在最後讓對象計數器-1,最後重寫delloc方法來檢查記憶體是否完全釋放。
6.多個物件記憶體管理
(1)多個物件記憶體管理規則:
只要還有人在用某個對象,那麼這個對象就不會被回收
只要你想用這個對象,就讓對象的計數器+1
當你不再使用這個對象時,就讓對象的計數器-1
(2)setter方法記憶體管理規則:
retain需要使用的對象
release之前的對象
只有傳入的對象和之前的不同才需要release和retain
- (void)setRoom:(Room *)room{ // 避免過度釋放(判斷私人成員和局部成員是否相等) if (room != _room) { // 對當前正在使用的車(舊車)做一次release [_room release]; // _room = nil; // 對新車做一次retain操作 _room = [room retain]; }}
3.dealloc方法的記憶體管理規則
- (void)dealloc{ // 當人不在了,代表不用房間了 // 對房間做一次release操作 [_room release]; // 這樣寫逼格高一點 self.room = nil; [super dealloc];}
[email protected]參數
(1)控制set方法的記憶體管理
retain : release舊值,retain新值(用於OC對象)
assign : 直接賦值,不做任何記憶體管理(預設,用於非OC物件類型)
copy : release舊值,copy新值(一般用於NSString *)
(2)控制需不需要產生set方法
readwrite :同時產生set方法和get方法(預設)
readonly :只會產生get方法
(3)多線程管理
atomic :效能低(預設)
nonatomic :效能高(iOS開發中都用這個屬性)
(4)控制set方法和get方法的名稱
setter : 設定set方法的名稱,一定有個冒號:
getter : 設定get方法的名稱
若有bool類型時最好修改getter方法為:(getter = isXXX)
注意: 不同類型的參數可以組合在一起使用
(5)循環參考
當使用@property屬性聲明兩個對象時,如果同時使用retain,會到時相互引用,記憶體不會釋放,解決辦法是,一個用retain,一個用assign。
8.autoreleasepool 自動釋放池
(1)在iOS程式運行過程中,會建立無數個池子。這些池子都是以棧結構存在(先進後出),當一個對象調用autorelease方法時,會將這個對象放到棧頂的釋放池。
(2)autorelease是一種支援引用計數的記憶體管理方式,只要給對象發送一條autorelease訊息,會將對象放到一個自動釋放池中,當自動釋放池被銷毀時,會對池子裡面的所有對象做一次release操作
注意
這裡只是發送release訊息,如果當時的引用計數(reference-counted)依然不為0,則該對象依然不會被釋放。
(3)autorelease使用注意事項
- 並不是放到自動釋放池代碼中,都會自動加入到自動釋放池
- 在自動釋放池的外部發送autorelease 不會被加入到自動釋放池中
autorelease是一個方法,只有在自動釋 放池中調用才有效。
- 如果寫了autorelease就不要寫release
- 只要在自動釋放池中調用autorelease, 就會將對象放入自動釋放池
- 自動釋放池中不適宜放佔用記憶體比較大的對象
- 不要連續調用autorelease,同時也不要把大量迴圈操作放到同一個 @autoreleasepool 之間
@autoreleasepool {// 建立對象時用autoreleasePerson *p =[ [Person alloc]init]autorelease];}
// 類方法+(instancetype)person{return [[self alloc]init]autorelease];}
// 類Factory 方法 +(instancetype)personWithAge:(int)age{ return [[[self alloc] initWithAge:age] autorelease];}
-(void)dealloc{ NSLog(@"%s", __func__); [super dealloc];}
七、ARC 自動引用計數管理記憶體
Automatic Reference Counting:(ARC)
自動引用計數,即ARC,可以說是WWDC2011和iOS5所引入 的最大的變革和最激動人心的變化。ARC是新的LLVM 3.0編譯器的一項特性,使用ARC,可以說一 舉解決了廣大iOS開發人員所憎恨的手動記憶體管理的麻煩。
1.ARC機制判斷注意點及優點
ARC機制判斷,ARC機制下有幾個明顯的標誌:
- 不允許調用對象的release方法
- 再重寫父類的dealloc方法時,不能再調用 [super dealloc];
ARC的注意點
- ARC是編譯器特性,而不是運行時特性
- ARC不是其它語言中的記憶體回收,有著本質區別ARC的
優點
- 完全消除了手動管理記憶體的煩瑣
- 基本上能夠避免記憶體泄露有時還能更加快速,因為編譯器還可以執行某些最佳化
2.強指標,弱指標
強指標
預設所有指標變數都是強指標
被__strong修飾的指標
Person *p1 = [[Person alloc] init]; __strong Person *p2 = [[Person alloc] init];
弱指標
被__weak修飾的指標
__weak Person *p = [[Person alloc] init];
3.ARC下單對象記憶體管理
(1)局部變數釋放對象隨之被釋放
(2)清null 指標對象隨之被釋放
(3)預設清空所有指標都是強指標
弱指標需要明確說明 :
注意: 千萬不要使用弱指標儲存新建立的對象
4.ARC下循環參考問題
與MRC一樣,當兩個對象相互引用時,會出現記憶體泄露的問題,解決辦法是:一個用strong一個用weak。
5.ARC下@property參數
- strong: 用於OC對象, 相當於MRC中的retain
- weak: 用於OC對象, 相當於MRC中的assign
- assign: 用於基礎資料型別 (Elementary Data Type), 跟MRC中的assign一樣(預設值)
6.如何將MRC轉換為ARC
在Xcode4.2 之前採用的時MRC記憶體管理機制,所以遇到MRC項目時可以採用Xcode裡的小工具進行轉換ARC,但是不敢保證百分之百成功,所以操作需謹慎。
具體操作如下:
·
·
如何進行ARC和MRC的混合使用:
轉變為非ARC -fno-objc-arc
轉變為ARC的, -f-objc-arc (不常用)
以上就是本人對Objective C記憶體管理的系統總結和分析,希望能為看到這篇文章的你有所協助,另外,有需要資料的夥伴們,新浪微博私信?關注極客James
著作權聲明:本文為博主原創文章,為了能相互促進,相互學習,請關注新浪微博:極客James
iOS核心語言Objective C語言 —— 記憶體管理