有场景A跟场景B,
场景A是当前场景,场景B是将要替换的新场景。那么A场景的资源释放最佳时机是在什么时候呢?这是释放资源的代码(注意要按这个顺序释放): 1 2 3 4 | CCAnimationCache::purgeSharedAnimationCache(); SpriteFrameCache::getInstance()->removeUnusedSpriteFrame(); CCTextureCache::getInstance()->removeUnusedTextures(); CCTextureCache::getInstance()->getCachedTextureInfo(); |
1.最开始我想到的,应该是在场景A的onExit里释放,试验了下,资源并没有被释放,说明当前纹理仍然在被引用,即场景A的子节点并未remove。后来查了下,发现每个场景的资源释放都是在一个叫dealloc的方法里,而这个方法是在onExit之后执行的。也就是说在onExit里释放资源是不合适的做法。2.我突然想起来,每个类不都有个析构函数么?析构函数是在该类的对象被delete时调用的,只要在A场景的析构函数里释放不就可以了?试验了下,发现也没效果,仔细想想,我创建场景用的方法是Cocos2DX推荐的默认方法,即场景类继承一个Layer,然后再createScene里创建一个Scene,再把当前类添加到Scene里。替换场景后,该Layer被释放时,场景中的其他资源并不一定被释放,所以此方法也是行不通的。3.竟然场景真正remove子节点是在dealloc方法里,那么在dealloc里释放资源不就好?可惜的是我根本就找不到有这个方法。于是放弃了。后来百度了下场景的调用顺序规则:
1 2 3 4 5 6 7 | replaceScene : SceneB init : SceneB onExitTransitionDidStart : SceneA onExit : SceneA dealloc : SceneA onEnter : SceneB onEnterTransitionDidFinish : SceneB |
由此可看出,B场景的onEnter函数在A场景的dealloc之后执行。而且那么只要在B场景的onEnter里释放A场景的资源就可以。试验了下,资源的确被释放了。而且,释放资源是在B场景的init之后执行,这样的好处就是,假设场景B中用到场景A中的某些资源,这些资源就不会被释放再加载,造成不必要的内存高峰。4.问题到此结束了吗?我也以为结束了,然后又发现新问题。一旦我给场景过渡加上效果,以上方法就不灵验了,释放失败。
1 | Director::getInstance()->replaceScene(CCTransitionFade::create(0.5f, SceneB::createScene())); |
然后调试跟踪下执行顺序,发现问题所在了。当有过渡效果存在的时候,执行顺序变成这样:
1 2 3 4 5 6 | replaceScene : SceneB init : SceneB onExitTransitionDidStart : SceneA onEnter : SceneB onExit : SceneA onEnterTransitionDidFinish : SceneB |
没错,场景A并不会立刻结束,而是等到动画效果完毕后才结束。但是我发现不管有没有使用过渡动画,最后执行的总是onEnterTransitionDidFinish,那么我们只要将资源释放放在这里就行了。经过试验,发现又失败了。。。为啥,因为虽然dealloc方法是在onExit之后执行,但并不是紧追其后,也就是说,有可能在onEnterTransitionDidFinish 之前,也有可能在其后,因此我们直接在onEnterTransitionDidFinish 里释放资源并不一定可行。那该怎么解决?这里我使用了延时释放的方法,在onEnterTransitionDidFinish 执行后过一段时间,再调用释放资源的方法,这样就可以确保资源被释放。
1 | scheduleOnce(CC_SCHEDULE_SELECTOR(SceneB::release), 2.0f); |
5.以上方法的确是可以将资源释放掉,但是不是我们所要的”最佳时机”呢?仔细观察,B场景的init是在A场景的释放之前的,也就是说,在B场景诞生,A场景彻底释放的短时间内,会存在A,B场景的所有资源共存的现在。这个时候内存达到巅峰。如果A场景跟B场景的资源所占内存都非常大的时候,或许会造成崩溃。那么我们应该如何解决?目前我的解决方法就是在场景过渡的时候增加一个过渡场景,也就是Loading场景,Loading场景本身所需的资源并不多,在A场景资源释放完毕后,再开启B场景的资源加载。