cocos2d-x場景切換時內存過高導致crash 解決方法

轉自:http://blog.csdn.net/zhangjingyangguang/article/details/7618048


最近在做一個cocos2d-x的項目時,遇到一個問題,就是在pc上運行都是ok的,可是在ipadandriod上面,在場景切換時時常會掛掉,用蘋果自帶的Instruments工具檢測時,發現在場景正常運行時,內存大概保持在三四十兆,但是在場景切換時,一瞬間會達到七八十兆,遇到一些素材比較多或者層比較多的場景,則會達到一百多兆。大家知道在ipad1上面,內存最大是128M,那麼這個程序如果在ipad1上面運行,肯定會經常掛掉。遇到問題,只能一步步分析,一步步找。

1先把所有場景的retain的東西檢查一遍,看看在Onexit的時候有沒有release掉,這個檢查完以後,還是會crash,所以這種情況不是根源。

2對於有好幾個層的場景,在init的時候,先加載第一個層的,而不是把所有層的東西全部加載完,在切換層的時候再加載相對應的層,這種方法果然有效,當切換到這個場景的時候,內存果然減少了一半左右。但是對於只有一個層的場景來說,在切換時也是會掛,所以問題還沒有找完全。

3我們知道,在每個場景裏面會有一個init函數,一個onEnterTransitionDidFinish函數,一個Onexit函數,init實現一些初始化工作,onEnterTransitionDidFinishinit之後執行,Onexit在場景退出時回收init時分配的資源。在調試時發現一個很有趣的現象,那就是從場景一切換到場景二時,在切換的一瞬間會內存會非常高,但是過了一段時間後,內存會回到一個平穩的狀態,譬如切換時內存會達到80M,切換過後內存會降到50M。分析原因,懷疑是上一個場景的內存還沒有釋放,然後這一個場景的內存已經分配,所以兩個疊加在一起,就比較高了。所以我便在第一個場景的Onexit函數中加一個斷點,在第二個場景的initonEnterTransitionDidFinish函數中各加一個斷點,然後運行程序,發現程序先到第二個場景的init中,然後再回到第一個場景的Onexit中,最後纔到第二個場景的onEnterTransitionDidFinish中。我才恍然大悟,原來在場景切換時,不是馬上會執行第一個場景的Onexit函數,而是先到第二個場景的init中加載資源,然後回到第一個場景中釋放資源,最後纔是到onEnterTransitionDidFinish中。

最終解決方法:

把一些資源的初始化放到onEnterTransitionDidFinish中進行,那麼究竟應該把那些資源放到onEnterTransitionDidFinish中初始化,而那些資源又只能放到init中呢?

1像背景圖這種只能放到init中,像場景切換時要看到的一些精靈,必須放到init中,不然場景切換時會看不到背景或者一些精靈。

2象精靈的一些動畫,動作,可以放到onEnterTransitionDidFinish中來初始化。

舉個例子:

譬如一隻船在划動,那麼船這隻精靈在場景切換時要展示,所以必須放在init

//小船精靈的加載

[cpp] view plaincopy

  1. m_boatAction = CCSprite::spriteWithFile(s1_little_boat1);  

  2.     addChild(m_boatAction);  

  3.     m_boatAction->setPosition( CCPointMake(s.width/2, s.height/2+130));  

  4.     m_boatAction->setScale(0.3);  

  5.     m_boatAction->retain();  


 

而船划動的動作,就可以放到onEnterTransitionDidFinish來初始化和執行

[html] view plaincopy

  1. CCSize s = CCDirector::sharedDirector()->getWinSize();  

  2.     CCAnimation* animation = CCAnimation::animation();  

  3.     char frameName[100] = {0};  

  4.     for( int i=1;i<=5;i++)  

  5.     {  

  6.         sprintf(frameName, "scene1/little_boat%d.png", i);  

  7.         animation->addFrameWithFileName(frameName);  

  8.     }  

  9.   

  10.     CCActionInterval*  action = CCAnimate::actionWithDuration(2, animation, false);  

  11.     repeatAction = CCRepeatForever::actionWithAction(action);  

  12.     repeatAction->retain();  

通過這三步,基本上就可以避免在場景切換時內存過而導致crash的情況

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

轉自:http://blog.csdn.net/tt5267621/article/details/8939020


最近在開發過程中遇到很多切換場景的時候概率性崩潰,內存暴增的問題。

因此總結一些開發中需要注意的要點,

1. 切換全屏場景的時候最好使用replaceScene而不是pushScene。

因爲pushScene並不會銷燬前一個scene,僅僅是將後一個scene按照堆棧的方式加入到前一個scene的上面。如果自身代碼中內存管理寫的不好的,利用pushScene很難發現該方面的問題,一旦崩潰定位都很難定位。replaceScene可以及早的將隱含的問題給暴露出來。

2. 儘量不要在onEnter裏面初始化精靈Sprite

這個就和上一條有點關係了,如果使用了pushScene,那麼再popScene的時候是不會調用前一個場景的init方法的,所以有同學就喜歡把一些初始化放在onEnter裏面,具體爲什麼不好,我們來看一下不同切換場景的時候,每個Scene的生命週期就知道了。

假設scene A是活動場景,現在我們用scene B來pushScene替換A,A和B的生命週期是這樣的:

B ---- init();

A ---- onExit();

B ---- onEnter();

B ---- onEnterTransitionDidFinish();

此時popScene,彈出scene B,函數調用如下:

B ---- onExit();

B ---- 析構函數被調用

A ---- onEnter();

從上面可以看出以下幾點,

1. A的析構函數始終未被調用,因此A一直在內存中。

2. 先執行B的init()函數,之後才調用A的onExit()方法,再之後才調用B的onEnter();所以初始化最好應該放在init中來初始化。在上一個場景退出之前初始化好後一個場景需要的資源。

同樣的,我們再來看一下replaceScene切換場景,scene的生命週期

假設scene A是活動場景,現在我們用scene B來replaceScene替換A,A和B的生命週期是這樣的:

B ---- init();

A ---- onExit();

A ---- 析構函數被調用

B ---- onEnter();

B ---- onEnterTransitionDidFinish();


此時B replace A回來的調用跟上面一樣,如下:

A ---- init();

B ---- onExit();

B ---- 析構函數調用

A ---- onEnter();


--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

還可以在a和b場景切換時,創建第三個場景來實現第二個場景所用資源的加載,這樣可以減輕內存暴漲

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章