OC 的內存管理
一、 基本原理
1. 什麼是內存管理
移動設備的內存及其有限,每個app所能佔用的內存是有限制的;
當app所佔用的內存較多時,系統會發出內存警告,這時得回收一些不需要在使用的內存空間,比如回收一些不需要使用的對象;
管理範圍:任何繼承了NSObject的對象,對其他基本數據類型(int char float double struct enum等)無效;
2. 對象的基本結構
每個OC對象都有自己的引用計數器,是一個整數,表示“對象被引用的次數”, 即有多少人正在使用這個OC對象;
每個OC對象內部專門有4個字節的存儲空間來存儲引用計數器;
當對象剛被創建的時間 存儲計數器默認值爲1;
3. 引用計數器的作用
當使用alloc、new或者copy創建一個新對象時,新對象的應用計數器默認就是1
當一個對象的引用計數器值爲0時,對象佔用的內存就會被系統回收,換句話說,如果對象不爲0,那麼在整個程序運行過程,他佔用的內存就不可能被回收,除非整個程序已經退出
4. 引用計數器的操作
給對象發送一條relain消息,可以使用計數器值+1(relain方法返回對象本身);
給對象發送一條release消息,可以使用引用計數器-1
可以給對象發送一條retainCount消息獲得當前的引用計數器的值
5. 對象的銷燬
當一個對象的引用計數器值爲0時,那麼他將被銷燬,其佔用的內存被系統回收;
當一個對象被銷燬時,系統會自動向對象發送一條dealloc消息;
一般會重寫dealloc方法,在這裏釋放相關資源,deallic就像對象的遺言;
一旦重寫了dealloc方法,就必須調[superdealloc],並且放在最後面調用
不要直接調用dealloc方法;
一旦對象被回收了,它佔用的內存空間就不再可用,堅持使用會導致程序崩潰(野指針錯誤);
【野指針:指向殭屍對象(不可用內存)的指針】【EXC_BAD_ACCESS:訪問了一塊壞的內存(已經被回收、已經不可用的內存),也稱爲:野指針錯誤 解決方法:p=nil 清空指針】
OC中不存在空指針錯誤;
【messagesent to deallocated instance 0*100109a10】
【給已經釋放的對象發送了一條-setAge消息:】
【如果對象已經變成殭屍對象之後不能ratain方法】
6.方法的基本使用
a) Retain:計數器+1 ,返回值是對象本身;
b) Release : 計數器+1,沒有返回值;
c) retainCount :獲取當前的計數器的值;
d) dealloc
l 當一個對象被回收的時候,就會調用;
l 一定要調用[super dealloc];這句調用要放在最後面;
7.部分概念
a) 殭屍對象:所佔有內存已經被回收的對象,殭屍對象不能再使用;
b) 野指針:指向殭屍對象(不可用內存)的指針,給野指針發送消息會報錯;
c) 空指針:沒有指向任何東西的指針(存儲的東西是nil、0),給空指針發送消息不會報錯;
二、 內存管理原則
1. 原則分析
l QQ糖開房間原理:只要房間還有人在用就不會解散
l 只要還有人在用某個對象,那麼這個對象就不會被回收
l 只要你想用這個對象,就讓對象的計數器+1
l 當你不再使用這個對象時,就讓計數器-1
2. 誰創建,誰release
l 如果你通過alloc、new或[mutable]copy來創建一個對象,那麼你必須調用release或者autorelease;
l 換句話說,不是你創建的,就不用你去[auto]release;
3. 誰retain,誰release
l 只要你調用了retain,無論這個對象是如何生成的,你都要調用release;
4. 總結
l 有始有終,有加就有減;
l 曾經讓對象的計數器+1,就必須在最後讓對象計數器-1;
三、 set方法的內存管理
如果你有個oc對象類型的成員變量,就必須管理這個成員變量的內存。比如有個Book *_book
內存管理代碼規範:
1.只要調用alloc,必須有release(autorelease)
2.set方法的代碼規範
1> 基本數據類型:直接複製
-(void)setAge:(int)age
{
_age=age;
}
2>OC對象類型
-(void)setCar:(Car *)car
{
//先判斷是不是薪傳進來對象
if( car !=_car)
{
[_car release];
_car = [car retain];
}
}
3>dealloc方法代碼規範
1. 一定要[super dealloc],而且放到最後面
2. 對self(當前)所擁有的其他對象做一次release
-(void)dealloc
{
[_carrelease];
[superdealloc];
}
4>@property內存管理
@property (retain) Bool *book;
@property中的參數
1) 內存管理相關的參數
retain :release舊值,retain新值(適用於OC對象類型)
assign:直接賦值(默認,適用於非OC對象類型)
copy :release舊值,copy新值
2)是否要生成set方法
readonly :只會聲明get的聲明和實現
readwrite :同時生成set和get的聲明和實現
3)多線程管理
nonatomic :性能高(一般就用這個)
atomic :性能低(默認)
4)set和get方法的名稱:
getter= 方法名;一般用於BOOL類型的數值
setter= 方法名;(一定要記住“:”)
四、 循環引用
@class
1.@class的作用:僅僅告訴編譯器某個名稱是一個類
@class Person //僅僅告訴編譯器Person是一個類
2.開發中引用一個類的規範
a) 在.h文件中用@class來聲明類
b) 在.m文件中用#import來包含類的所有東西
3.【面試題】
@class 和#import的區別
#import方法會包含被引用類的所有信息,包括被引用類的變量和方法;@class方法只是告訴編譯器在A.h文件中B *b只是類的聲明,具體這個類裏面有什麼信息,這裏不需要知道,等實現文件中真正要用到時,纔會真正去查看B類中信息
如果有上百個頭文件都用#import了同一個文件,或者這些文件依次被#import那麼一旦最開始的頭文件稍有改動,後面引用到這個文件的所有列都需要重新編譯一遍,這樣的效率也是可想而知的,而相對來講,使用@class方式就不會出現這種問題了
在.m實現文件中,如果需要引用到被引用類的實體變量或者方法時,還需要使用#import方式引入被引用類
【面試題】循環引用的解決方法:
兩端循環引用解決方案:
一端用retain,一端用assign
五、autorelease方法:半自動釋放
Person *p = [[[Person alloc] init] autorelease];
autorelease方法返回對象本身
調用autorelease方法後,對象的計數器不變
autorelease會將對象放到一個自動釋放池中
當自動釋放池被銷燬時,會對池子裏面的所有對象做一次release操作
@autoreleasepool
{
//傳說中的自動釋放池的創建
//自動釋放池可以創建無限多個
//不能精確控制釋放時間
}
@autorelease的基本用發
A.會將對象放到一個自動釋放池中
B.當自動釋放池被銷燬時,會對池子裏面的所有對象做一次release操作
C.會返回對象本身
D.調用完autorelease方法後,對象的計數器不變
@autorelease的優點
A.不用在關心對象釋放的時間
B.不用在關心什麼時候調用release
@autorelease的使用注意
A.佔用內存較大的對象不要隨便用autorelease
B.佔用內存較小的對象使用autorelease沒有太大的影響
【面試題】:
情況一:autoreleasepool{
Person *p = [[[Person alloc]init]autorelease];
[p release];
}
情況二:Person*p = [[[[Person alloc] init]autorelease]autorelease]
這種連續調用多次autorelease就會出現野指針錯誤
自動釋放池
A.在ios程序運行過程中,會創建無數個池子。這些池子都是以棧結構存在(先進後出)
B.當一個對象調用autorelease方法時,會將這個對象放到棧頂的釋放池
Ios 5.0 之前的釋放池格式
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pool release];//[pool drain]此釋放用於mac
IOS5.0之後的釋放池格式
@autoreleasepool
{
}
1.系統自帶的方法裏面沒有包含alloc、new、copy,說明返回的對象都是autorelease的
2.開發中經常會提供一些類方法,快速創建一個已經autorelease方法
a) 創建對象時不要直接用各類名,一般用self
+person
{
Return[[[selfalloc] init] autorelease];
}
六、 ARC
Arc 的判斷標準:只要沒有強指針指向對象,就會釋放對象
指針分2 種:
1.強指針:默認情況下,所有指針都是強指針
2.弱指針:__weak
在arc中retain換成strong
總結
1.ARC特點
a) 不允許調用release、retain、ritainCount
b) 允許重寫dealloc,但是不允許調用[superdealloc]
c) @property的參數
l strong : 成員變量是強指針,相當於原來的retain(適用 於OC對象類型)
l weak:成員變量是弱指針,相當於原來的assign(適用於OC 對象類型)
l assign:適用於非OC對象類型
d) 以前的retain改爲strong ,其他的一切不變
Refactor 重構
ARC的轉換 xcode菜單 Edit-----Refactor-----轉換成ARC
單獨處理非ARC文件轉換 -fno-objc-arc
轉換成ARC文件 -f-objc-arc
ARC的循環引用:
一端用strong一端用weak