標籤:
一、記憶體管理黃金法則:
The basic rule to apple is everything thatincreases the reference counter with alloc,[mutable]copy[WithZone:] or retainis in charge of the corresponding [auto]release.
如果一個對象使用了alloc,[mutable] copy,retain,那麼你必須使用相應的release或autonrelease
二、記憶體管理類型分類
基本類型和C語言的類型:如:
int,short,char,struct,enum,union等類型
OC類型:任何繼承於NSObject對象都屬於OC的類型。
我們講的記憶體管理實際上是對OC類型的記憶體管理,它對基礎資料型別 (Elementary Data Type)和C語言的類型並不管用。
三、C和C++記憶體管理的不足
如上,又3個引用指向了這一塊記憶體,任何一個調用了free方法釋放這個記憶體,而其餘的引用在不知道的情況下繼續使用這塊記憶體的時候,就會出現問題。何時由誰去釋放這塊記憶體,這就是C和C++在記憶體管理上的混亂。
四、OC對象在記憶體中的結構
所有的OC類型的對象的結構如下,這個對象的記憶體在包含自己的變數和方法的基礎上,還有一個包含retainCount的引用計數。
每一個OC對象都有一個4個位元組的retainCount的計數器。表示當前對象被引用的計數。如果計數為0,那麼就真正的去釋放這個對象。
規則:
1、Objective-C類中實現了引用計數器,對象知道自己當前被引用的次數
2、最初對象的計數器為1
3、如果需要引用對象,可以給對象發送一個retain訊息,這樣對象的計數器就加1
4、當不需要引用對象了,可以給對象發送release訊息,這樣對象計數器就減1
5、當計數器減到0,自動調用對象的dealloc函數,對象就會釋放記憶體
6、計數器為0的對象不能再使用release和其他方法
五、舉例說明
比如有一個引擎類Engine,有一個汽車類Car,Car裡面有一歌Engine的執行個體變數,一個setter和getter方法。具體如下
[plain] view plaincopy
- #import "Car.h"
-
- @implementation Car
-
-
- -(void)setEngine:(Engine*) engine
- {
- _engine=engine;
- }
- -(Engine*)engine
- {
- return _engine;
- }
-
- -(void)dealloc
- {
- NSLog(@"Car is dealloc");
- [super dealloc];
- }
- @end
上面寫的是一個簡單的類,當讓這樣寫是有問題,所以需要一步步的改進。
第一步改進:
先使用它看問題的所在,在main方法裡面如下使用:
[plain] view plaincopy
- //先建立一個引擎
- Engine* engine1=[[Engine alloc]init];
- [engine1 setID:1];
- //在建立一個汽車,設定汽車的引擎
- Car* car=[[Car alloc]init];//retainCount=1
- [car setEngine:engine1];
- /*分析:在這裡,現在有兩個引用指向這個Engine對象,engine1和Car中的_engine,可是這個Engine對象的引用計數還為1,因為在
- set方法中,並沒有使用retain。那麼不管是哪個引用調用release,那麼另外一個引用都會指向一塊釋放掉的記憶體,那麼肯定
- 會發生錯誤。所以需要在set方法中加以改進。*/
第二步改進:
setter方法改進
[plain] view plaincopy
- -(void)setEngine:(Engine*) engine
- {
- _engine=[engine retain];//多了一個引用,retainCount+1
- }
再在main中使用它
[plain] view plaincopy
- //先建立一個引擎
- Engine* engine1=[[Engine alloc]init];
- [engine1 setID:1];
- //在建立一個汽車,設定汽車的引擎
- Car* car=[[Car alloc]init];//retainCount=1
- [car setEngine:engine1];//retainCount=2,因為使用了retain,所以retainCount=2,
-
- //假設還有一個引擎
- Engine* engine2=[[Engine alloc]init];
- [engine2 setID:2];
-
- //這個汽車要換一個引擎,自然又要調用settr方法
- [car setEngine:engine2];
-
- /*分析:在這裡,汽車換了一個引擎,那麼它的_engine就不在指向engine1的哪個對象的記憶體了,而是換成了engine2,也就是說engine1的哪個對象指向的記憶體的引用只有一個
- 可是它的retainCount是兩個,這就是問題的所在了。所以仍然需要改進*/
第三步改進:
[plain] view plaincopy
- -(void)setEngine:(Engine*) engine
- {
- [_engine release];//在設定之前,先release,那麼在設定的時候,就會自動將前面的一個引用release掉
- _engine=[engine retain];//多了一個引用,retainCount+1
- }
再在main‘中使用
[plain] view plaincopy
- //先建立一個引擎
- Engine* engine1=[[Engine alloc]init];
- [engine1 setID:1];
- //在建立一個汽車,設定汽車的引擎
- Car* car=[[Car alloc]init];//retainCount=1
- [car setEngine:engine1];//retainCount=2,因為使用了retain,所以retainCount=2,
-
- //如果進行了一個誤操作,又設定了一次engine1
- [car setEngine:engine1];
-
- /*分析:那麼,又要重新調用一次setter方法,這根本就是無意義的操作,浪費資源,所以要在設定之間加上判斷*/
第四步改進:
[plain] view plaincopy
- -(void)setEngine:(Engine*) engine
- {
- if(_engine!=engine){//判斷是否重複設定
- [_engine release];//在設定之前,先release,那麼在設定的時候,就會自動將前面的一個引用release掉
- _engine=[engine retain];//多了一個引用,retainCount+1
- }
- }
第五步:
現在setter方法基本沒有問題了,那麼在當我們要釋放掉一個car對象的時候,必須也要釋放它裡面的_engine的引用,所以,要重寫car的dealloc方法。
[plain] view plaincopy
- -(void)dealloc
- {
- [_engine release]; //在釋放car的時候,釋放掉它對engine的引用
- [super dealloc];
- }
這還不是最好的釋放的方法,下面的方法更好
[plain] view plaincopy
- -(void)dealloc
- {
- [_engine setEngine:nil]; //在釋放car的時候,對setEngine設定為nil,它不僅會release掉,並且指向nil,即使誤操作調用也不會出錯。
- [super dealloc];
- }
所以,綜上所述,在setter方法中的最終寫法是
[plain] view plaincopy
- <span style="color:#CC66CC;">-(void)setEngine:(Engine*) engine
- {
- if(_engine!=engine){//判斷是否重複設定
- [_engine release];//在設定之前,先release,那麼在設定的時候,就會自動將前面的一個引用release掉
- _engine=[engine retain];//多了一個引用,retainCount+1
- }
- }</span>
然後在dealloc方法中寫法是:
[plain] view plaincopy
- <span style="color:#CC66CC;">-(void)dealloc
- {
- [_engine setEngine:nil]; //在釋放car的時候,對setEngine設定為nil,它不僅會release掉,並且指向nil,即使誤操作調用也不會出錯。
- [super dealloc];
- }</span>
六、property中的setter文法關鍵字
在property屬性中有三個關鍵字定義關於展開setter方法中的文法,assgin(預設),retain,copy。當然這三個關鍵字是互斥的。
1、assgin展開stter的寫法
[plain] view plaincopy
- -(void)setEngine:(Engine*) engine
- {
- _engine=engine;
- }
2、retain展開的寫法
[plain] view plaincopy
- -(void)setEngine:(Engine*) engine
- {
- if(_engine!=engine){//判斷是否重複設定
- [_engine release];//在設定之前,先release,那麼在設定的時候,就會自動將前面的一個引用release掉
- _engine=[engine retain];//多了一個引用,retainCount+1
- }
- }
3、copy展開的寫法
[plain] view plaincopy
- -(void)setEngine:(Engine*) engine
- {
- if(_engine!=engine){//判斷是否重複設定
- [_engine release];//在設定之前,先release,那麼在設定的時候,就會自動將前面的一個引用release掉
- _engine=[engine copy];//多了一個引用,retainCount+1
- }
- }
對於copy屬性有一點要主要,被定義有copy屬性的對象必須要符合NSCopying協議,並且你還必須實現了-(id)copyWithZone:(NSZone*)zone該方法。
可以看到,使用retain和我們上面舉得例子完全相同,所以我們可以使用property和它的retain代替之前的寫法。
Objective-C的記憶體管理(一)黃金法則的理解
Objective-C的記憶體管理(一)黃金法則的理解