天天看點

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

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

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

先來看最初的版本:

Engine.h

Engine.m

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

Car.h

Car.m

解釋一下:init方法中,給每輛汽車在出廠時預置了一個預設的引擎(其flag值為預設值0),然後setEngine方法用于給汽車設定新引擎,最後dealloc中,汽車銷毀時會附帶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方法有了第二個版本:

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

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

同樣有二個問題:

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

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

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

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

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

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

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資源最終都得到了釋放!

2014-02-21 注:現在最新的xcode上,setEngine方法不管是第二個版本,還是第三個版本,對于第三種情況,都能正确釋放所有資源。估計是xcode做了改進,這二個版本編譯出來的代碼,個人估計是相同的,原書上的分析應該是基于當時的xcode環境,友情提醒大家學習時注意。

上一篇: CSS盒子模型
下一篇: CSS盒子模型

繼續閱讀