看完了OC視訊裡面的記憶體管理,之前對于記憶體管理很陌生,可以說幾乎沒有接觸過,才知道寫代碼記憶體管理的重要性,下面簡單總結一下學習筆記
1.retain跟release的基本概念
像一般的資料類型int、double、char,這種類型的資料是系統自動回收記憶體空間的,但是OC對象是需要手動回收的(不用ARC機制),怎麼回收呢?
記憶體管理的原理:每個OC對象都有自己的引用計數器,是一個整數,表示“對象被引用的次數”,在對象内部有4個位元組的存儲空間存儲引用計數器,當使用alloc、new或者copy建立一個新對象時,新對象的引用計數器預設為1,當一個對象的引用計數器值為0時,對象占用的記憶體就會被系統回收,否則一直不會回收,直到程式退出。
那計數器怎麼加1,怎麼減1呢?
給對象發送一條 retain消息,可以使引用計數器值+1;給對象發送一條 release消息,可以使引用計數器值-1;而給對象發送一條 retainCount消息,擷取目前引用計數器的值
那retain、release使用規則是什麼呢?
使用retain 加1,release減1是有規則的,不能随便亂加亂減的:使用某一個對象,就讓對象的計數器+1,讓對象做一次retain操作,不再使用對象,就讓對象的計數器-1,讓對象做一次release;并且誰retain 誰release;誰alloc 誰release
當一個對象被銷毀時,系統會自動向對象發送一條dealloc消息。可以重寫dealloc方法,但是不能直接調用dealloc方法。
#import<Foundation/Foundation.h>
@interface Person:NSObject
@end
@implementation Person
- (void) dealloc
{
NSLog(@"對象被回收");
[super dealloc]; // 一定要調用父類的dealloc,并且放在重寫dealloc代碼的最後
}
@end
int main()
{
Person *p = [[Person alloc] init];
NSUInteger c = [p retainCount];
NSLog(@"計數器:%ld",c);
[p retain]; // 傳回對象本身p,計數器+1
[p release]; // 沒有傳回值,計數器-1
[p release]; // alloc對應的release
// 釋放完了對象,不能在一直釋放了,會報野指針的錯誤
return 0;
}
上面這段代碼是簡單的alloc、retain、release的應用示例,所謂的野指針錯誤是指:指向了僵屍對象(不可用記憶體)的指針,僵屍對象是指所占用記憶體已經被回收的對象,不可用的對象。為了防止指針變成野指針,在沒有ARC機制的情況下,一般是對象回收後,加 p = nil; 将指針清空,變成空指針。OC中給空指針發送消息不會報錯,不存在空指針錯誤。
2.多對象記憶體管理
多對象記憶體管理是在一個對象擁有另外一個對象的情況下對記憶體的管理
#import <Foundation/Foundation.h>
@interface Book:NSObject
{
int _price;
}
@end
@implementation Book
- (void)dealloc
{
NSLog(@"書被回收了");
[super dealloc];
}
@end
@interface Person:NSObject
{
Book *_book;
}
- (void)setBook:(Book *)book;
- (Book *)book;
@end
@implementation Person
- (void)setBook:(Book *)book
{
_book = [book retain]; // 當把一個對象指派給_book時,說明_book使用這個對象,那此對象的計數器就要加1
}
- (Book *)book
{
return _book;
}
- (void)dealloc
{
[_book release]; // 當人這個對象被回收了,他擁有的書的資源也要釋放了,是以讓書對象的計數器減1
NSLog(@"人被回收了");
[super dealloc];
}
@end
int main()
{
Book *b = [[Book alloc] init];
Person *p = [[Person alloc] init];
[p setBook:b];
[b release]; // 書對象的計數器減1,b不再指向書對象
b = nil; // 清空指針
[p release]; // 人對象的計數器減1,為0,記憶體回收,回收前執行dealloc,将書對象的計數器也減1,書對象也回收
p = nil;
return 0;
}
3.property的記憶體管理
我們前面已經講過,setter跟getter都是通過property來自動生成的,那再property裡面怎麼來時候記憶體管理呢?前面那個例子我們假設人要換一本書了,是不會對原來的書做一次release的,因為人沒有銷毀,是以set方法還需要完善
- (void)setBook:(Book *)book
{
if (_book != book)
{
_book release;
_book = [book ratain];
}
}
而property的@property(retain)Book *book;這一句相當于前面一段set方法,是以又可以利用property來自動生成setter跟getter,還可以做好記憶體管理。但是dealloc還是要寫的
- (void)dealloc
{
[_book release];
[super dealloc];
}
property的四大類參數
1.set方法記憶體管理相關的參數
retain:release舊值,retain新值(适用于OC對象類型)
assign:直接指派(預設,适用于非OC對象類型)
copy:release舊值,copy新值
2.是否要生成set方法
readonly: 隻生成getter的聲明、實作
readwrite:同時生成getter和setter的聲明、實作(預設)
3.多線程管理
nonatomic:性能高(一般要加上的)
atomic:性能低(預設)
4.setter和getter方法的名稱,設定方法名稱
@property(getter = abc,setter = setAbc:) int weight
4.autorelease
autorelease是對象方法,傳回對象本身,可以取代release,調用autorelease方法,會将對象放到一個自動釋放池中,當自動釋放池被銷毀時,會對池子裡面的所有對象做一次release;調用autorelease方法後,對象的計數器不變,等釋放池銷毀才會release一次。可以建立很多個釋放池,并且釋放池可以嵌套,釋放池在記憶體中以棧結構存放。
int main()
{
@autoreleasepool{ // 建立了釋放池
Person *p = [[[Person alloc] init ] autorelease];
p.age = 10;
} // 代表銷毀釋放池
autorelease的錯誤寫法:
1.alloc之後調用了autorelease,又調用了release
@autoreleasepool
{
Person *p = [[[Person alloc] init] autorelease];
[p release];
}
2.連續調用多次autorelease
@autoreleasepool
{
Person *p = [[[[Person alloc] init] autorelease] autorelease];
}
}
5.ARC
利用arc記憶體管理機制,很大程度上取代了人為記憶體管理。ARC判斷準則:隻要沒有強指針指向對象,就會釋放對象。預設情況下所有的指針都是強指針, 弱指針_weak 。
ARC特性:
1.不允許調用release、retain、retainCount
2.允許重寫dealloc,但是不允許調用[super dealloc]
3.strong weak 都隻适用于OC對象類型,在OC對象類型裡,weak相當于assign的作用
-weak Person *p = [[Person alloc] init]; 沒有意義的寫法,對象一建立就被釋放了,弱指針也清空了,p裡面是null。
在arc機制中,以@property(nonatomic,strong) Dog *dog; 替代 @property(nonatomic,retain) Dog *dog;
#import <Foundation/Foundation.h>
@interface Person:NSObject
@property(nonatomic,weak) Dog *dog;
@end
@implementation Person
@end
int main()
{
Dog *d = [[Dog alloc] init];
Person *p = [[Person alloc] init];
p.dog = d ; // p對象裡面的_dog成員變量也指向d對象,但是是弱指針
d = nil; // d指針一清空,沒有強指針指向狗對象,狗對象釋放記憶體
NSLog(@"%@",p.dog);
return 0;
}