標籤:objective-c 單個對象記憶體管理 手動記憶體管理
———————————————————————————————————————————
單個對象記憶體管理
(1)野指標
①定義了一個指標變數,但是並沒有賦初值,它隨機指向一個東西
②某指標變數指向的記憶體空間被釋放掉了(指向殭屍對象的指標)
(2)殭屍對象
已經被銷毀的對象(無法被使用的對象)
(3)null 指標
沒有指向儲存空間的指標(裡面存的是nil,也就是0)
給null 指標發訊息是沒有任何反應的,不會提示出錯
代碼:
#import <Foundation/Foundation.h>
@interface Person : NSObject
-(void)run;
@end
@implementation Person
-(void)run
{
NSLog(@"run!");
}
- (void)dealloc
{
NSLog(@"retainCount的結果為0,對象記憶體被釋放!");
[super dealloc];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p=[[Person alloc]init];
NSLog(@"[p retainCount]=%tu",[p retainCount]); //值為1
[p run];
NSLog(@"[p retainCount]=%tu",[p retainCount]); //值還是為1,當前只有一個使用對象p的,也就是他自己,引用對象p的次數並沒有增加
[p retain];
NSLog(@"[p retainCount]=%tu",[p retainCount]); //值為2
[p release];
NSLog(@"[p retainCount]=%tu",[p retainCount]); //值為1
[p release];//這裡的計數器值為0,記憶體被釋放
// [p run]; //記憶體釋放之後run方法還是可以調用的
// 那麼問題來了,上面run方法是在對象p的堆區記憶體被釋放之後調用的,為什麼還會調用成功呢?現在我們就來解決也是上節課遺留的問題
// 首先當p對象被release之後(記憶體被釋放),p就是一個野指標(殭屍對象)了
// 我們知道,雖然p在堆區的記憶體空間被釋放了,但是記憶體空間是不會變的還在那兒,棧區中也存在p。只是p已經失去了那一部分記憶體的管理權。如果此時你非要去使用這塊空間的話,且這塊記憶體空間沒有分配給其他的程式,其實還是能擷取到內容的。但是如果這塊釋放了的空間又被分配給其他的變數或者程式,這時候使用就不對了。
// 而且還有一點,就是上面釋放之後又調用run方法,不一定能百分百的調用,可能10次會出現一次調用錯誤的情況,這也就是野指標的危險。所以說,就不能用殭屍對象調用!!調用是完全無意義的!
// [p retain];
// 這樣是完全錯誤的,我們上面已經將p所指向的對象記憶體釋放,那麼該對象就成為了殭屍對象,我們就不能夠這樣讓其複活(這樣硬來得到的值也是錯誤的結果)
// 為了避免我們使用殭屍對象,我們可以做這樣一個處理
p=nil;
// 這樣處理過後,編譯器在執行下列語句的時候就不會報錯了。
[p run];//不會執行,因為p為nil
[p retain];//也不會讓計數+1,還是因為p為nil(空值)
NSLog(@"[p retainCount]=%tu”,p.retainCount);
//點文法( 這裡相當於將p.retainCount——>[p retainCount] )
}
return 0;
}
★那麼我們應該怎麼防止野指標的調用呢?
操作步驟就是上面的兩幅圖了,將第二幅表徵圖記的地方打勾,那麼再使用野指標運行就會報錯了!
Enable Zombie Objects 就是開啟殭屍模式~
★★★關於nil和Nil以及NULL的區別
nil:首先這是一個null 指標,而且這是一個OC的對象,是一個對象值,如果我們把一個對象設為空白的話,我們就要設為nil ( #define nil ((id) 0) )
Nil:這是一個類對象值,如果把一個類對象設為空白,那麼我們就要設為Nil
NULL:是一個通用指標(泛型指標) ( #define NULL ((void *) 0 ) )
NSNull: [ NSNull null ] 是一個對象,用在不能使用nil的場合
還有最後一點我們 需要注意的,那就是當我們釋放了一個對象的記憶體空間,讓該對象成為了殭屍對象,那麼是不能夠 用 [p retain]; 讓它複活的。在上面的程式中有體現。
(4)記憶體流失
代碼:
#import <Foundation/Foundation.h>
@interface Car : NSObject
-(void)run:(Car *)cccc;
@end
@implementation Car
- (void)dealloc
{
NSLog(@"釋放記憶體!");
[super dealloc];
}
-(void)run:(Car *)cccc
{
NSLog(@"車在跑");
[cccc retain];//在方法內部使用retain讓cccc的計數+1
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// (4)記憶體流失問題
// ① retain 的次數 和 release 的次數 不匹配
// Car *car=[Car new];
// NSLog(@"car->retainCount=%tu",car.retainCount);//1
//
// [car retain];
// [car retain];
// [car retain];
//
// NSLog(@"car->retainCount=%tu",car.retainCount);//4
//
// [car release];
//
// NSLog(@"car->retainCount=%tu",car.retainCount);//3
//// 顯然 retain 的次數 和 release 的次數 是不匹配的。retainCount 不為0,那麼空間自然沒有被回收釋放
//// 如果不想讓記憶體流失,那麼必須 retain + new = release (增加引用計數=減少引用計數)
//// ②對象在使用過程中被賦值了nil
// Car *car1=[Car new];
//
// [car1 retain];
// [car1 retain];
// [car1 retain];
// NSLog(@"car1->retainCount=%tu",car1.retainCount);//4
//
// car1=nil;
// [car1 release];//其實這四條release語句是沒有作用的 ,是 [nil release]; ,我們知道向nil發送什麼指令都不會報錯,但是此時原對象car1的空間是沒有被釋放的,所以說最後car1的記憶體還是被泄漏了。
// [car1 release];
// [car1 release];
// [car1 release];
//
// NSLog(@"car1->retainCount=%tu",car1.retainCount);//0,此時的0說明不了任何問題,並不是我們成功釋放記憶體,也不會列印dealloc中的語句
// ③在方法中不當的使用了retain
// Car *car2=[Car new];
// [car2 run:car2];//我們將 car2 自己作為參數傳進去run方法,然後在run方法的內部retain,那麼car2->retainCount=2,所以說這裡如果只release是不夠的,應該release兩次才能釋放完記憶體
}
return 0;
}
———————————————————————————————————————————
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
Objective-C 【單個對象記憶體管理】