【Cocos2d-x 3.x】内存管理机制

【Cocos2d-x 3.x】内存管理机制,第1张

概述侯捷先生说过这么一句话 :  源码之前,了无秘密。 要了解Cocos2d-x的内存管理机制,就得阅读源码。 接触Cocos2d-x时, Cocos2d-x的最新版本已经到了3.2的时代,在学习Cocos2d-x 3.x的时,经常会写点很小的例子,比如创建一个精灵Sprite, 然后设计精灵的动作Action等等,或者添加图层Layer并设置相应属性等等。在创建这些元素的时候,都会先进行这样的 *** 作

侯捷先生说过这么一句话 : 源码之前,了无秘密。 要了解Cocos2d-x的内存管理机制,就得阅读源码。

接触Cocos2d-x时, Cocos2d-x的最新版本已经到了3.2的时代,在学习Cocos2d-x 3.x的时,经常会写点很小的例子,比如创建一个精灵Sprite, 然后设计精灵的动作Action等等,或者添加图层Layer并设置相应属性等等。在创建这些元素的时候,都会先进行这样的 *** 作 :

cocos2d::Sprite*	m_sprite 	= 	cocos2d::Sprite::create("Picture_name.png");cocos2d::Layer* 	m_layer		=	cocos2d::Layer::create();

在cocos2d-x 3.0之后加入了C++11的内容,于是m_sprite和m_layer的类型就可以这样写了 :
auto	m_sprite 	= 	cocos2d::Sprite::create("Picture_name.png");auto 	m_layer		=	cocos2d::Layer::create();

当然这些都不是重点, 重点的是这些直接或间接继承Node的类(Node继承自Ref)在创建的时候都使用 create() 成员函数来创建, 看看create()的源码 :
Node * Node::create(){    Node * ret = new (std::nothrow) Node();    if (ret && ret->init())    {        ret->autorelease(); // 重点在这里    }    else    {        CC_SAFE_DELETE(ret);    }    return ret;}


在create一个对象时, 会调用一个autorelease()函数, 继续跟进 :

Ref* Ref::autorelease(){    PoolManager::getInstance()->getCurrentPool()->addobject(this);    return this;}

原来是将新创建好的这个添加到当前的管理池中,而addobject实际上是Ref类的函数,Ref中有个存放Ref* 类型的vector,将新创建的对象添加到vector之中。

PoolManager::getInstance()->getCurrentPool()->addobject(this)这行代码中,涉及到了三个类 : PoolManager, autoreleasePool和Ref类。实际上, Cocos2d-x的内存管理机制就是和这三个类息息相关的,分别来看下源码 :


先看Ref类:

// CCRef.cppRef::Ref(): _referenceCount(1) // 新创建一个Ref,将引用计数初始化为 1{#if CC_ENABLE_SCRIPT_BINDING    static unsigned int uObjectCount = 0;    _luaID = 0;    _ID = ++uObjectCount;    _scriptObject = nullptr;#endif    #if CC_REF_LEAK_DETECTION    trackRef(this);#endif}Ref::~Ref(){#if CC_ENABLE_SCRIPT_BINDING    // if the object is referenced by Lua engine,remove it    if (_luaID)    {        ScriptEngineManager::getInstance()->getScriptEngine()->removeScriptObjectByObject(this);    }    else    {        ScriptEngineProtocol* pEngine = ScriptEngineManager::getInstance()->getScriptEngine();        if (pEngine != nullptr && pEngine->getScriptType() == kScriptTypeJavaScript)        {            pEngine->removeScriptObjectByObject(this);        }    }#endif#if CC_REF_LEAK_DETECTION    if (_referenceCount != 0)        untrackRef(this);#endif}// retain函数将引用计数值增加 1voID Ref::retain(){    CCASSERT(_referenceCount > 0,"reference count should be greater than 0");    ++_referenceCount;}// release函数将引用计数值减少 1voID Ref::release(){    CCASSERT(_referenceCount > 0,"reference count should be greater than 0");    --_referenceCount;    // 如果引用计数等于0, 如果此时自动管理池仍然在清理该对象,则直接报错(assert)    if (_referenceCount == 0)    {#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)        auto poolManager = PoolManager::getInstance();        if (!poolManager->getCurrentPool()->isClearing() && poolManager->isObjectInPools(this))        {                        CCASSERT(false,"The reference shouldn't be 0 because it is still in autorelease pool.");        }#endif#if CC_REF_LEAK_DETECTION        untrackRef(this);#endif        delete this;    }}// 将此对象交个自动管理池来管理Ref* Ref::autorelease(){    PoolManager::getInstance()->getCurrentPool()->addobject(this);    return this;}unsigned int Ref::getReferenceCount() const{    return _referenceCount;}


再看autoreleasePool类:

// autoreleasePool.cppautoreleasePool::autoreleasePool(): _name("")#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0),_isClearing(false)#endif{	// 初始化时将Ref* 的vector大小设置为150    _managedobjectArray.reserve(150);    // 然后将这个自动管理池添加到管理类PoolManager中    PoolManager::getInstance()->push(this);}// 具有特定名字的管理池  方便调试autoreleasePool::autoreleasePool(const std::string &name): _name(name)#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0),_isClearing(false)#endif{    _managedobjectArray.reserve(150);    PoolManager::getInstance()->push(this);}// 当前管理池销毁时, 从管理类中d出该管理池autoreleasePool::~autoreleasePool(){    cclOGINFO("dealLocing autoreleasePool: %p",this);    clear();        PoolManager::getInstance()->pop();}// 添加对象到vector中voID autoreleasePool::addobject(Ref* object){    _managedobjectArray.push_back(object);}voID autoreleasePool::clear(){#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)    _isClearing = true;#endif    for (const auto &obj : _managedobjectArray)    {        obj->release();    }    _managedobjectArray.clear();#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)    _isClearing = false;#endif}bool autoreleasePool::contains(Ref* object) const{    for (const auto& obj : _managedobjectArray)    {        if (obj == object)            return true;    }    return false;}voID autoreleasePool::dump(){    cclOG("autorelease pool: %s,number of managed object %d\n",_name.c_str(),static_cast<int>(_managedobjectArray.size()));    cclOG("%20s%20s%20s","Object pointer","Object ID","reference count");    for (const auto &obj : _managedobjectArray)    {        CC_UNUSED_ParaM(obj);        cclOG("%20p%20u\n",obj,obj->getReferenceCount());    }}

最后看看PoolManager类:
// CCPoolManager.cppPoolManager* PoolManager::s_singleInstance = nullptr;PoolManager* PoolManager::getInstance(){    if (s_singleInstance == nullptr)    {        s_singleInstance = new (std::nothrow) PoolManager();		//assert(nullptr != s_singleInstance);        // 在创建单例时, 添加第一个管理池        new autoreleasePool("cocos2d autorelease pool");    }    return s_singleInstance;}// 销毁单例模式voID PoolManager::destroyInstance(){    delete s_singleInstance;    s_singleInstance = nullptr;}// 初始化时设置管理类的vector的大小为10PoolManager::PoolManager(){    _releasePoolStack.reserve(10);}// 一个管理类PoolManager销毁时,销毁它管理的所有管理池PoolManager::~PoolManager(){    cclOGINFO("dealLocing PoolManager: %p",this);        while (!_releasePoolStack.empty())    {        autoreleasePool* pool = _releasePoolStack.back();                delete pool;    }}// 获得当前管理池autoreleasePool* PoolManager::getCurrentPool() const{    return _releasePoolStack.back();}// 判断一个Ref或其子类对象是否在管理池中bool PoolManager::isObjectInPools(Ref* obj) const{    for (const auto& pool : _releasePoolStack)    {        if (pool->contains(obj))            return true;    }    return false;}// 添加一个管理池voID PoolManager::push(autoreleasePool *pool){    _releasePoolStack.push_back(pool);}// d出最新的一个管理池voID PoolManager::pop(){    CC_ASSERT(!_releasePoolStack.empty());    _releasePoolStack.pop_back();}

源码都在这里了,重新分析开头说的那个例子,在创建一个Sprite或Layer时, 先调用autorelease函数 :

Ref* Ref::autorelease(){    PoolManager::getInstance()->getCurrentPool()->addobject(this);    return this;}

然后就会先获得一个管理自动管理内存池的类PoolManager的单例 :
PoolManager* PoolManager::getInstance(){    if (s_singleInstance == nullptr)    {        s_singleInstance = new (std::nothrow) PoolManager();		//assert(nullptr != s_singleInstance);        // Add the first auto release pool        new autoreleasePool("cocos2d autorelease pool");    }    return s_singleInstance;}

第一次使用单例,会新建一个,会添加一个自动管理池 :
autoreleasePool::autoreleasePool(const std::string &name): _name(name)#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0),_isClearing(false)#endif{    _managedobjectArray.reserve(150);    PoolManager::getInstance()->push(this);}

然后将该管理池push到PoolManager中的vector中,该vector是管理autoreleasePool的 :
voID PoolManager::push(autoreleasePool *pool){    _releasePoolStack.push_back(pool); // std::vector<autoreleasePool*> _releasePoolStack;}

然后由该单例获得管理这个Sprite或Layer的autoreleasePool,并将该Sprite或Layer添加到该自动管理池当中 :
voID autoreleasePool::addobject(Ref* object){    _managedobjectArray.push_back(object); // std::vector<Ref*> _managedobjectArray;}

然后如果将这个Sprite添加到图层时,会增加这个Sprite的引用计数 :
voID pushBack(T object){    CCASSERT(object != nullptr,"The object should not be nullptr");    _data.push_back( object );    object->retain(); // 增加引用计数}


remove一个Sprite,会减少该对象的引用计数:

voID clear(){    for( auto it = std::begin(_data); it != std::end(_data); ++it ) {        (*it)->release();    }    _data.clear();}


在autoreleasePool类中,建议不要在堆上建立内存管理池,因为new出来的需要手动delete掉,而栈上的内存管理池则在程序结束后自动销毁:

autoreleasePool::~autoreleasePool(){    cclOGINFO("dealLocing autoreleasePool: %p",this);    clear();        PoolManager::getInstance()->pop();}

当前池销毁后, 就从管理类中d出该池。


自动管理池用clear()函数来释放 , 有Director类来控制自动管理池的释放 *** 作:

voID displaylinkDirector::mainLoop(){    if (_purgeDirectorInNextLoop)    {        _purgeDirectorInNextLoop = false;        purgeDirector();    }    else if (_restartDirectorInNextLoop)    {        _restartDirectorInNextLoop = false;        restartDirector();    }    else if (! _invalID)    {        drawScene();             // release the objects        PoolManager::getInstance()->getCurrentPool()->clear();    }}

_invalID来决定Director是否应该进行逻辑循环,_purgeDIErctorInNextLoop在Director调用了end()函数后被设置为true,_restartDirector在restart()后被设置为true,mainloop函数是游戏的主逻辑循环处理函数, drawScene函数进行处理逻辑帧在每帧尾结束都调用clear函数来清理这一帧所占用的资源,每帧都会回收,如果上一帧进行很多次autorelease而没有帧帧来清理,内存池的性能就会急剧下降,典型的例子是:魂斗罗里主角的“S”型子d,一帧内产生了几十个子d资源,如果在这一帧结束后不释放,内存池的性能就会急剧下降,游戏就会显得非常卡。


我们知道,游戏运行都是从Application开始的,在Application中的函数run来将游戏运行起来,在run里面,有个死循环不断地进行directo->mainloop()执行逻辑 *** 作。

从而进行内存的释放。

以上就是我个人对内存管理的浅显的理解,如有不足,欢迎指出~

总结

以上是内存溢出为你收集整理的【Cocos2d-x 3.x】内存管理机制全部内容,希望文章能够帮你解决【Cocos2d-x 3.x】内存管理机制所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://www.54852.com/web/1074052.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-26
下一篇2022-05-26

发表评论

登录后才能评论

评论列表(0条)

    保存