菜鳥學習Cocos2d-x 3.x——內存管理
亙古不變的東西
到現在,內存已經非常便宜,但是也不是可以無限大的讓你去使用,特別是在移動端,那麼點內存,那麼多 APP要搶著用,搞不好,你佔的內存太多了,系統直接幹掉你的APP,所以說了,我們又要老生常談了——內存管理。總結COM開發的時候,分析過COM的 內存管理模式;總結Lua的時候,也分析了Lua的內存回收機制;前幾天,還專門寫了C++中的智能指針在內存使用方面的應用;可見,內存管理無論是語言 層面,還是類庫層面,都有嚴格的標準和實施,對於Cocos2d-x來說,也是如此。那麼在Cocos2d-x中,它是如何進行內存管理的呢?這篇文章, 我就來總結一下關於Cocos2d-x的內存管理方面的知識。讓你輕鬆度過面試官的五指關(面試Cocos2d-x時,100%會問到的問題啊,上點心 吧)。
初窺Cocos2d-x內存管理
對於探究內存管理這種比較抽象的東西,最簡單的方法就是通過代碼來研究,首先通過創建一個簡單的場景來看看Cocos2d-x在完成創建一個對象的時候,它都幹了些什麼。
創建一個Scene:
函數
create
是一個靜態函數,看看create
函數的源碼:
現在就涉及到了Cocos2d-x的內存管理相關的知識了。在Cocos2d-x中,關於對象的創建與初始化都是使用的new和
init
函數搭配的方式,這種方式叫做二段式創建,由於C++中,構造函數沒有返回值,無法通過構造函數確定初始化的成功與失敗,所以在Cocos2d-x中就大行其道的使用了這種二段式創建的方式,用起來還不錯,以後在自己的項目中,也可以採用。
由於這種方式在Cocos2d-x中經常被使用,所以觸控那幫傢伙就搞了個宏:
CREATE_FUNC
。如果想讓我們的類也使用這種二段式創建的方式,只需要在我們的類中加入以下代碼:
同時,需要定義一個init函數,這就OK了。我們來看看這個宏:
話說這些東西也都是基礎的C++知識,沒有多少需要說的了,當你看到代碼中的
ret->autorelease()
,一臉茫然,是的,你已經看到了Cocos2d-x的內存管理的觸角了。ret->autorelease()
是什麼?當我使用create函數創建了場景以後,我並沒有去delete,這也沒有問題。問題就發生在這個autorelease
的使用上。序幕說完了,讓我們真正的開始Cocos2d-x的內存管理吧。
在Cocos2d-x中,關於內存管理的類有:
- Ref類;
- AutoreleasePool類;
- PoolManager類。
Ref類幾乎是Cocos2d-x中所有類的父類,它是Cocos2d-x中內存管理的最重要的一環;上面說的
autorelease
函數就Ref類的成員函數,Cocos2d-x中所有繼承自Ref的類,都可以使用Cocos2d-x的內存管理。
AutoreleasePool類用來管理自動釋放對象。
PoolManager用來管理所有的AutoreleasePool,這個類是使用單例模式實現的。
下面就通過對上述三個類的源碼進行分析,看看Cocos2d-x到底是如何進行內存管理的。
Ref類
先來看看Ref類的定義,以下是Ref類的頭文件定義:
對於
release
函數的實現,這裡需要特別總結一下,先看看它的實現:
上面也說道了,對於new和autorelease需要匹配使用,retain和release也需 要匹配使用,否則就會出現斷言錯誤,或者內存洩露;在非Debug模式下,就可能直接閃退了。這就是為什麼我們在使用create函數的時候,new成功 以後,就順便調用了autorelease,將該對象放入到自動釋放池中;而當我們再次想獲取該對象並使用該對象的時候,需要使用retain再次獲得該 對象的所有權,當然了,在使用完成以後,你應該記得調用release去手動完成釋放工作,這是你的任務。例如以下代碼:
這是錯誤的,在create中,在創建成功的情況下,已經將obj對象放到了autorelease pool中了;當你再次放入autorelease pool後,當銷毀autorelease pool以後,就會出現兩次銷毀一個對象的情況,出現程序的crash。再例如以下代碼也是錯誤的:
當使用create函數創建對象以後,obj沒有所有權,當再次調用release時,就會出現錯誤的對象釋放。而正確的做法應該如下:
這引用計數,又讓我想起了COM中的AddRef和Release。
AutoreleasePool類
AutoreleasePool類是Ref類的友元類,先來看看Autorelease類的聲明。
對於AutoreleasePool類來說,它的實現很簡單,就是將簡單的將對象保存在一個std::vector中,在釋放這個AutoreleasePool的時候,對保存在std::vector中的對象依次調用對應的release函數,從而完成對象的自動釋放。
PoolManager類
這貨又是干什麼的?當我們在閱讀AutoreleasePool的源碼的時候,在它的構造函數中,你會發現如下代碼:
在AutoreleasePool的析構函數中,又有如下代碼:
哦,原來,我們把AutoreleasePool對象又放到了PoolManager裡了;原 來,PoolManager類就是用來管理所有的AutoreleasePool的類,也是使用的單例模式來實現的。該PoolManger有一個存放 AutoreleasePool對象指針的stack,該stack是由std::vector實現的。需要注意的是,cocos2d-x的單例類都不是 線程安全的,跟內存管理緊密相關的PoolManager類也不例外,因此在多線程中使用cocos2d-x的接口需要特別注意內存管理的問題。關於更安 全的單例模式,感興趣的同學可以去閱讀這篇《C++設計模式——單例模式》。接下來,我們先看看PoolManager的頭文件定義。
關於PoolManager中各個函數的實現也是非常簡單的,這裡不做累述,各位可以去閱讀Cocos2d-x的源碼。
問題來了
說了這麼多,代碼也列了這麼多,我們create一個對象以後,放到了 AutoreleasePool中去了,最終,在調用AutoreleasePool的clear函數的時候,會對AutoreleasePool管理的 所有對象依次調用release操作。啊哈!貌似哪裡不對,我一直都沒有說最終誰會調用這個clear函數啊?是的。看下面這段在導演類中的代碼,我想你 會明白的。
上面的代碼說明的事實是:在圖像渲染的主循環中,如果當前的圖形對象是在當前幀,則調用顯示函數,並 調用AutoreleasePool::clear()減少這些對象的引用計數。mainLoop是每一幀都會自動調用的,所以下一幀時這些對象都被當前 的AutoreleasePool對象release了一次。這也是AutoreleasePool「自動」的來由。
總結
好了,總結的差不多了,對於Cocos2d-x中的內存管理總結的差不多了。對於Cocos2d-x 中的內存管理,我個人認為,請時刻關注著這個對象的引用計數,retain和release,new和autorelease需要匹配使用,防止不必要的 錯誤發生。總結了這麼多,還是那句話。
紙上得來終覺淺,絕知此事要躬行。
只有經過實際的使用,在經過代碼的洗練,才能更好的去掌握這些。在Cocos2d-x中,很多地方已 經進行了autorelease,或者retain了,我們就不必再次進行這些操作,比如create,再比如在調用addChild方法添加子節點時, 自動調用了retain。對應的通過removeChild,移除子節點時,自動調用了release。這些地方稍微不注意,就可能會讓你掉入「坑」中。 努力吧,夥計們。
沒有留言:
張貼留言