objective-C 的記憶體管理之-執行個體分析

來源:互聯網
上載者:User

註:這是《Objective-C基礎教程》一書上的執行個體,但是原書限於篇幅,分析得比較簡單,初次閱讀看得比較費勁,這裡展開詳細討論一下。

情境:有二個類Car和Engine,即“汽車”和“引擎”。

先來看最初的版本:

Engine.h

#import <Cocoa/Cocoa.h>@interface Engine : NSObject@property int flag;@end // Engine

Engine.m

#import "Engine.h"@implementation Engine@synthesize flag;- (NSString *) description{    return ([NSString stringWithFormat:@"I am engine %d,my retainCount=%d",flag,[self retainCount]]);} // description-(void) dealloc{NSLog(@"this engine %d is going to die.",flag);[super dealloc];NSLog(@"this engine %d is dead.",flag);}@end // Engine

代碼不複雜,略加解釋:Engine類有一個flag屬性,用於後面輔助輸出時區分當前引擎的唯一標識。然後就是description方法(相當於c#中Object的toString()方法),用於返回一個描述自身的字串。最後就是dealloc方法,用於清理自身所用的資源。

Car.h

#import <Cocoa/Cocoa.h>#import"Engine.h"@interface Car : NSObject{    Engine *engine;}@property int flag;- (void) setEngine: (Engine *) newEngine;- (Engine *) engine;@end // Car

Car.m

#import "Car.h"#import "Engine.h"@implementation Car@synthesize flag;- (id) init{    if (self = [super init]) {        engine = [Engine new]; //每輛汽車誕生時,先預設了一個空的引擎(flag=0的engine),這個對象最終也需要釋放!           }    return (self);} // init- (Engine *) engine{    return (engine);} // engine- (void) setEngine: (Engine *) newEngine{engine = newEngine;    } // setEngine-(void) dealloc{NSLog(@"the car %d is going to die.",flag);NSLog(@"%@",engine);[engine release];//釋放附屬資源:引擎[super dealloc];NSLog(@"the car %d is dead.",flag);}@end // Car

解釋一下:init方法中,給每輛汽車在出廠時預置了一個預設的引擎(其flag值為預設值0),然後setEngine方法用於給汽車設定新引擎,最後dealloc中,汽車銷毀時會附帶release自己的引擎。

先來考慮第一種情況:

有一輛汽車,給它安裝了新引擎,使用完後汽車銷毀,但是引擎還能拿出來做其它用途(比如給其它汽車使用之類),最後新引擎也用完了,銷毀!

Car *car1 = [Car new];car1.flag  = 1;Engine *engine1 = [Engine new];engine1.flag = 1;[car1 setEngine:engine1];[car1 release];NSLog(@"%@",engine1);//這裡類比引擎做其它用途[engine1 release];

以上代碼至少有二個問題:

1.1 Car在建構函式init裡,預置的預設引擎(即flag=0的引擎)最後未被釋放

1.2 Car在dealloc方法中,已經釋放了engine,所以Car釋放後,該引擎也就跟著灰飛煙滅了,沒辦法再做其它用途。所以第7,8行代碼根本沒辦法運行,會直接報錯!這比記憶體流失更嚴重。

先來解決最嚴重的第2個問題,至少讓它跑起來再說,根源在於:Car銷毀時,附帶把engine也給release了!解決它的途徑有二種:

1、去掉Car.m類dealloc中的[engine release],但是本著“自家的孩子自己管”的原則,不推薦這種不負責任的做法。

2、在setEngine方法中,人工調用[newEngine retain]方法,讓引擎的引用計數加1,這樣正好可抵消Car.m類dealloc方法中[engine release]帶來的影響(一加一減,正好抵消!)。

於是Car.m中的setEngine方法有了第二個版本:

- (void) setEngine: (Engine *) newEngine{engine = [newEngine retain];    } // setEngine

再次編譯,總算通過了,也能運行了。先把問題1.1丟到一邊,再來考慮第二種情況:

又有一輛汽車,安裝了新引擎engine1,然後試了一下,覺得不爽,於是把engine1丟了,然後又換了另一個引擎engine2(喜新厭舊!)

Car *car1 = [Car new];car1.flag = 1;Engine *engine1 = [Engine new];engine1.flag = 1;[car1 setEngine:engine1];//換新引擎engine1[engine1 release];//覺得不爽,於是把engine1扔了Engine *engine2 = [Engine new];engine2.flag = 2;[car1 setEngine:engine2];//又換了新引擎engine2[car1 release];//使用完以後,car1報廢[engine2 release];//新引擎engine2當然也不再需要了

同樣有二個問題:

2.1 engine1先被new了一次,然後在setEngine中又被retain了一次,也就是說其retainCount為2,雖然代碼中後來release了一次,但是也只能讓retainCount減到1,並不能銷毀!

2.2 剛才1.1中所說的問題依然存在,即Car在init方法中預置的預設引擎engine0,始終被無視了,未得到解脫。

可能,你我都想到了,在setEngine方法中,可以先把原來的舊引擎給幹掉,然後再把新引擎掛上去,這樣就ok了! 好吧,setEngine的第三個版本出現了:

- (void) setEngine: (Engine *) newEngine{[engine release];engine = [newEngine retain];    } // setEngine

貌似皆大歡喜了,但是事情還沒完,又有新情況了:第三種情況

有二輛汽車Car1與Car2,Car1換了新引擎engine1,然後跑去跟Car2顯擺,Car2覺得新引擎不錯,於是要求跟Car1共用新引擎engine1,但問題是:在Car2尚未下手前,engine1已經被某人(可能是car1自己,也可能是車主main()函數)給拋棄了!

Engine *engine1 = [Engine new];//engine1.retainCount=1engine1.flag = 1;Car *car1 = [Car new];car1.flag = 1;Car *car2 = [Car new];car2.flag = 2;[car1 setEngine:engine1];//car1換了新引擎engine1[engine1 release];//然後很快又拋棄了它[car2 setEngine:[car1 engine]];//car2要跟car1共用engine1//最後car1跟car2都被車主main函數給扔了[car2 release];[car1 release];

問題:在16行[car2 release]時,car2已經徹底把engine1給銷毀了(也許car2忘記了,engine1是它跟car1共同的財產),於是緊接著[car1 release]時,car1的dealloc方法在[engine release]時,意外發現engine1已經不在人世了,最終它憤怒了,整個程式也就罷工了!

setEngine的最後一個版本

- (void) setEngine: (Engine *) newEngine{[newEngine retain];[engine release];engine = newEngine;    } // setEngine

其實就是把上一個版本的二行代碼,拆分成了三行,變成了先retain,再release,看上去好象含義一樣,但是仔細分析你會發現,如果當engine與newEngine為同一個對象的引用時(即這二指標指向的為同一塊記憶體),且newEngine(其實也就是engine)的retainCount為1時,原來的版本會導致newEngine(其實也就是engine)銷毀,而現在這樣處理後,即會被保留下來。

最後驗證一個最終版本是否能完美應付上面提到的三種情況:

第一種情況的運行結果:

2011-02-25 09:17:52.951 CarParts[257:a0f] this engine 0 is going to die.
2011-02-25 09:17:52.957 CarParts[257:a0f] this engine 0 is dead.
2011-02-25 09:17:52.959 CarParts[257:a0f] the car 1 is going to die.
2011-02-25 09:17:52.961 CarParts[257:a0f] I am engine 1,my retainCount=2
2011-02-25 09:17:52.962 CarParts[257:a0f] the car 1 is dead.
2011-02-25 09:17:52.966 CarParts[257:a0f] I am engine 1,my retainCount=1
2011-02-25 09:17:52.968 CarParts[257:a0f] this engine 1 is going to die.
2011-02-25 09:17:52.969 CarParts[257:a0f] this engine 1 is dead.

第二種情況的運行結果:

2011-02-25 09:19:30.639 CarParts[291:a0f] this engine 0 is going to die.
2011-02-25 09:19:30.644 CarParts[291:a0f] this engine 0 is dead.
2011-02-25 09:19:30.646 CarParts[291:a0f] this engine 1 is going to die.
2011-02-25 09:19:30.648 CarParts[291:a0f] this engine 1 is dead.
2011-02-25 09:19:30.650 CarParts[291:a0f] the car 1 is going to die.
2011-02-25 09:19:30.652 CarParts[291:a0f] I am engine 2,my retainCount=2
2011-02-25 09:19:30.653 CarParts[291:a0f] the car 1 is dead.
2011-02-25 09:19:30.655 CarParts[291:a0f] this engine 2 is going to die.
2011-02-25 09:19:30.657 CarParts[291:a0f] this engine 2 is dead.

第三種情況的運行結果:

2011-02-25 09:21:02.549 CarParts[324:a0f] this engine 0 is going to die.
2011-02-25 09:21:02.554 CarParts[324:a0f] this engine 0 is dead.
2011-02-25 09:21:02.556 CarParts[324:a0f] this engine 0 is going to die.
2011-02-25 09:21:02.558 CarParts[324:a0f] this engine 0 is dead.
2011-02-25 09:21:02.559 CarParts[324:a0f] the car 2 is going to die.
2011-02-25 09:21:02.561 CarParts[324:a0f] I am engine 1,my retainCount=2
2011-02-25 09:21:02.563 CarParts[324:a0f] the car 2 is dead.
2011-02-25 09:21:02.571 CarParts[324:a0f] the car 1 is going to die.
2011-02-25 09:21:02.573 CarParts[324:a0f] I am engine 1,my retainCount=1
2011-02-25 09:21:02.575 CarParts[324:a0f] this engine 1 is going to die.
2011-02-25 09:21:02.578 CarParts[324:a0f] this engine 1 is dead.
2011-02-25 09:21:02.587 CarParts[324:a0f] the car 1 is dead.

從輸出結果上看,不管是哪一種情況,Car以及Engine資源最終都得到了釋放!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.