------- android教育訓練、 java教育訓練、期待與您交流! ----------
Objective-C記憶體管理
基本資料類型存儲在棧中,系統會自動釋放存儲空間,而OC對象存儲在堆中,系統不會自動回收對象,故而引起記憶體管理。
1.引用計數器的基本操作
每一個對象都有一個引用計數器,它在記憶體中占用4個位元組
(1)方法的基本使用
1> retain :計數器+1,會傳回對象本身
2> release :計數器-1,沒有傳回值
3> retainCount :擷取目前的計數器
4> dealloc
當一個對象要被回收的時候,就會調用
一定要調用[super dealloc],這句調用要放在最後面
(2).概念
1> 僵屍對象 :所占用記憶體已經被回收的對象,僵屍對象不能再使用
2> 野指針 :指向僵屍對象(不可用記憶體)的指針,給野指針發送消息會報錯(EXC_BAD_ACCESS)
3> 空指針 :沒有指向任何東西的指針(存儲的東西是nil、NULL、0),給空指針發送消息不會報錯
Person p = [[Person alloc ] init];
[p retain];
[p release];
int size = [p retainCount];
[p release]
// 當一個Person對象被回收的時候,就會自動調用這個方法
- (void)dealloc
{
NSLog(@"Person對象被回收");
// super的dealloc一定要調用,而且放在最後面
[super dealloc];
}
心得體會:
(1)當使用alloc、new或者copy建立一個新對象時,新對象的引用計數器預設就是1
(2)當一個對象的引用計數器值為0時,那麼它将被銷毀,其占用的記憶體被系統回收
(3)不要直接調用dealloc方法
(4) 一旦對象被回收了,它占用的記憶體就不再可用,堅持使用會導緻程式崩潰(野指針錯誤)
(5)OC不存在空指針錯誤,給空指針發送消息,不報錯
2.set方法的記憶體管理
記憶體管理代碼規範:
1.隻要調用了alloc,必須有release(autorelease)對象不是通過alloc産生的,就不需要release
2.set方法的代碼規範
1> 基本資料類型:直接複制
- (void)setAge:(int)age
{
_age = age;
}
2> OC對象類型
- (void)setCar:(Car *)car
{
// 1.先判斷是不是新傳進來對象
if ( car != _car )
{
// 2.對舊對象做一次release
[_car release];
// 3.對新對象做一次retain
_car = [car retain];
}
}
3.dealloc方法的代碼規範
1> 一定要[super dealloc],而且放到最後面
2> 對self(目前)所擁有的其他對象做一次release
- (void)dealloc
{
[_car release];
[super dealloc];
}
心得體會:
如果你有個OC對象類型的成員變量,就必須管理這個成員變量的記憶體。
[email protected]
1.set方法記憶體管理相關的參數
retain : release舊值,retain新值(适用于OC對象類型)
assign : 直接指派(預設,适用于非OC對象類型)
copy : release舊值,copy新值
2.是否要生成set方法
readwrite : 同時生成setter和getter的聲明、實作(預設)
readonly : 隻會生成getter的聲明、實作
3.多線程管理
nonatomic : 性能高 (一般就用這個)
atomic : 性能低(預設)
4.setter和getter方法的名稱
setter : 決定了set方法的名稱,一定要有個冒号 :
getter : 決定了get方法的名稱(一般用在BOOL類型)
@interface Person : NSObject
// 傳回BOOL類型的方法名一般以is開頭
@property (getter = isRich) BOOL rich;
@property (nonatomic, assign, readwrite) int weight;
@property (readwrite, assign) int height;
@property (nonatomic, assign) int age;
@property (retain) NSString *name;
@end
心得體會:
(1)不同類型的參數可以組合使用。
(2)一般都會添加nonatomic參數,提高性能。
4.循環引用
當一個類引用了另外一個類,另一個類又引用了這個類的時候,就叫做循環引用。
@class Person;
@interface Card : NSObject
@property (nonatomic, assign) Person *person;
@end
#import "Card.h"
@interface Person : NSObject
@property (nonatomic, retain) Card *card;
@end
心得體會:這種代碼編譯會報錯。當使用@class在兩個類互相聲明,就不會出現編譯報錯
@class
僅僅是告訴編譯器,Card是一個類
@class和#include的差別:
(1)#import方式會包含被引用類的所有資訊,包括被引用類的變量和方法;@class方式隻是告訴編譯器在A.h檔案中 B *b 隻是類的聲明,具體這個類裡有什麼資訊,這裡不需要知道,等實作檔案中真正要用到時,才會真正去檢視B類中資訊
(2)@class相比#include而言,能提高效率。當#include引入的檔案更改時,所有引入的檔案都要重新編譯一遍
心得體會:
(1)在.m實作檔案中,如果需要引用到被引用類的實體變量或者方法時,還需要使用#import方式引入被引用類
(2)兩端循環引用的解決方案是,一端用retain一端用assign。
5.autorelease
(1) 給某個對象發送一條autorelease消息時,就會将這個對象加到一個自動釋放池中
(2)當自動釋放池銷毀時,會給池子裡面的所有對象發送一條release消息
(3)調用autorelease方法時并不會改變對象的計數器,并且會傳回對象本身
@autoreleasepool
{
// 1
Person *p = [[[Person alloc] init] autorelease];
}
1> alloc之後調用了autorelease,又調用release
@autoreleasepool
{
// 1
Person *p = [[[Person alloc] init] autorelease];
// 0
[p release];
}
// 2> 連續調用多次autorelease
@autoreleasepool
{
Person *p = [[[[Person alloc] init] autorelease] autorelease];
}
自動釋放池
(1) 在iOS程式運作過程中,會建立無數個池子。這些池子都是以棧結構存在(先進後出)
(2)當一個對象調用autorelease方法時,會将這個對象放到棧頂的釋放池
// 自動釋放池的建立方式
// 1> iOS 5.0前
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pool release]; // [pool drain];
// 2> iOS 5.0 開始
@autoreleasepool
{
}
心得體會:
(1)autorelease實際上隻是把對release的調用延遲了,對于每一次autorelease,系統隻是把該對象放入了目前的autorelease pool中,當該pool被釋放時,該pool中的所有對象會被調用Release。
6.autorelease應用
(1)系統自帶的方法裡面沒有包含alloc、new、copy,說明傳回的對象都是autorelease的。
NSString *str2 = [NSString stringWithFormat:@"age is %d", 10];
(2)開發中經常會提供一些類方法,快速建立一個已經autorelease過的對象
+ (id)person
{
return [[[self alloc] init] autorelease];
}
心得體會:
(1)建立對象時不要直接用類名,一般用self,這樣于子類調用。