一.內存管理的原因
(1)內存溢出 內存不夠用
(2)野指針異常 指針操作了不屬于自己的存儲空間,指針操作已經銷毀的對象
二.內存管理的方式
(1)MRC? 手動管理內存
(2)ARC? 自動管理內存
三.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];
}
7.@property參數
這里寫圖片描述
(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 {
// 創建對象時用autorelease
Person *p? =[ [Person alloc]init]autorelease];
}
// 類方法
+(instancetype)person{
return [[self alloc]init]autorelease]
}
// 類工廠方法
+(instancetype)personWithAge:(int)age
{
return [[[self alloc] initWithAge:age] autorelease];
}
-(void)dealloc
{
NSLog(@"%s", __func__);
[super dealloc];
}
四、ARC 自動引用計數管理內存
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)清空指針對象隨之被釋放
(3)默認清空所有指針都是強指針
弱指針需要明確說明 :
注意: 千萬不要使用弱指針保存新創建的對象
4.ARC下循環引用問題
與MRC一樣,當兩個對象相互引用時,會出現內存泄露的問題,解決辦法是:一個用strong一個用weak。
5.ARC下@property參數
strong: 用于OC對象, 相當于MRC中的retain
weak: 用于OC對象, 相當于MRC中的assign
assign: 用于基本數據類型, 跟MRC中的assign一樣(默認值)
五.如何將MRC轉換為ARC
如何進行ARC和MRC的混合使用:
轉變為非ARC -fno-objc-arc
轉變為ARC的, -f-objc-arc (不常用)