cocos2dx 3.x 源码阅读之一 引用计数原理

cocos2dx 3.x 源码阅读之一 引用计数原理,第1张

概述 之前看过很多的文章,都是讲解cocos2dx的引用计数原理。有好有坏,今天我也自己来写一篇引用计数的文章,并且开始尝试阅读cocos2dx的源码,把自己的收获更新到这个博客,不为多少人看。只为自己记录这一路的点点滴滴。 讲了这么多那么开始吧! 首先就是贴上cocos2dx的万物之源 Ref类,这个类是Node的基类,而所有的显示节点类都是继承自Node类,所以他算是cocos2dx的根。 CCR

之前看过很多的文章,都是讲解cocos2dx的引用计数原理。有好有坏,今天我也自己来写一篇引用计数的文章,并且开始尝试阅读cocos2dx的源码,把自己的收获更新到这个博客,不为多少人看。只为自己记录这一路的点点滴滴。

讲了这么多那么开始吧!

首先就是贴上cocos2dx的万物之源 Ref类,这个类是Node的基类,而所有的显示节点类都是继承自Node类,所以他算是cocos2dx的根。

CCRef.h

class CC_DLL Ref{public:    voID retain();    voID release();    Ref* autorelease();    unsigned int getReferenceCount() const;protected:    Ref();public:    virtual ~Ref();protected:    //用于计数的变量    unsigned int _referenceCount;    frIEnd class autoreleasePool;#if CC_ENABLE_SCRIPT_BINDINGpublic:    unsigned int        _ID;    int                 _luaID;    voID* _scriptObject;#endif    #if CC_USE_MEM_LEAK_DETECTIONpublic:    static voID printLeaks();#endif};
CCRef.cpp
#include "base/CCRef.h"#include "base/CCautoreleasePool.h"#include "base/ccMacros.h"#include "base/ccScriptSupport.h"#if CC_USE_MEM_LEAK_DETECTION#include <algorithm>    // std::find#endifNS_CC_BEGIN#if CC_USE_MEM_LEAK_DETECTIONstatic voID trackRef(Ref* ref);static voID untrackRef(Ref* ref);#endifRef::Ref(): _referenceCount(1) // when the Ref is created,the reference count of it is 1{#if CC_ENABLE_SCRIPT_BINDING    static unsigned int uObjectCount = 0;    _luaID = 0;    _ID = ++uObjectCount;    _scriptObject = nullptr;#endif    #if CC_USE_MEM_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_USE_MEM_LEAK_DETECTION    if (_referenceCount != 0)        untrackRef(this);#endif}voID Ref::retain(){    CCASSERT(_referenceCount > 0,"reference count should greater than 0");    ++_referenceCount;}voID Ref::release(){    CCASSERT(_referenceCount > 0,"reference count should greater than 0");    --_referenceCount;    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_USE_MEM_LEAK_DETECTION        untrackRef(this);#endif        delete this;    }}Ref* Ref::autorelease(){    PoolManager::getInstance()->getCurrentPool()->addobject(this);    return this;}unsigned int Ref::getReferenceCount() const{    return _referenceCount;}#if CC_USE_MEM_LEAK_DETECTIONstatic std::List<Ref*> __refAllocationList;voID Ref::printLeaks(){    if (__refAllocationList.empty())    {        log("[memory] All Ref objects successfully cleaned up (no leaks detected).\n");    }    else    {        log("[memory] WARNING: %d Ref objects still active in memory.\n",(int)__refAllocationList.size());        for (const auto& ref : __refAllocationList)        {            CC_ASSERT(ref);            const char* type = typeID(*ref).name();            log("[memory] LEAK: Ref object '%s' still active with reference count %d.\n",(type ? type : ""),ref->getReferenceCount());        }    }}static voID trackRef(Ref* ref){    CCASSERT(ref,"InvalID parameter,ref should not be null!");    __refAllocationList.push_back(ref);}static voID untrackRef(Ref* ref){    auto iter = std::find(__refAllocationList.begin(),__refAllocationList.end(),ref);    if (iter == __refAllocationList.end())    {        log("[memory] CORRUPTION: Attempting to free (%s) with invalID ref tracking record.\n",typeID(*ref).name());        return;    }    __refAllocationList.erase(iter);}#endif // #if CC_USE_MEM_LEAK_DETECTIONNS_CC_END


上面的代码中我删除了一些注释,为了避免干扰。

我们从一看出Ref这个类中有一个

protected:    //用于计数的变量    unsigned int _referenceCount;
这个变量就是记录引用次数,并且在构造函数中初始化时1。

在代码retain中可以看到只有简单增加这个计数器。

重要的来了,那就是release这个函数,我们看到他是在计数器为0的时候,delete自己。这样的设计源于自己创建自己销毁这个原则。

我们还可以看到 autoRelease 这个接口,他调用了PoolManager。

从字面上猜到,这个接口的作用应该是自动释放。

那么他是怎么实现的呢?

我们又找到了PoolManager的源码。

CCautoreleasePool.h

#ifndef __autoRELEASEPOol_H__#define __autoRELEASEPOol_H__#include <stack>#include <vector>#include <string>#include "base/CCRef.h"NS_CC_BEGINclass CC_DLL autoreleasePool{public:    autoreleasePool();        autoreleasePool(const std::string &name);        ~autoreleasePool();    voID addobject(Ref *object);    voID clear();    #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)    bool isClearing() const { return _isClearing; };#endif        bool contains(Ref* object) const;    voID dump();    private:    std::vector<Ref*> _managedobjectArray;    std::string _name;    #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)    bool _isClearing;#endif};class CC_DLL PoolManager{public:    CC_DEPRECATED_ATTRIBUTE static PoolManager* sharedPoolManager() { return getInstance(); }    static PoolManager* getInstance();    CC_DEPRECATED_ATTRIBUTE static voID purgePoolManager() { destroyInstance(); }    static voID destroyInstance();        autoreleasePool *getCurrentPool() const;    bool isObjectInPools(Ref* obj) const;    frIEnd class autoreleasePool;    private:    PoolManager();    ~PoolManager();        voID push(autoreleasePool *pool);    voID pop();        static PoolManager* s_singleInstance;        std::vector<autoreleasePool*> _releasePoolStack;};NS_CC_END#endif //__autoRELEASEPOol_H__
CCautoreleasePool.cpp
#include "base/CCautoreleasePool.h"#include "base/ccMacros.h"NS_CC_BEGINautoreleasePool::autoreleasePool(): _name("")#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0),_isClearing(false)#endif{    _managedobjectArray.reserve(150);    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);}autoreleasePool::~autoreleasePool(){    cclOGINFO("dealLocing autoreleasePool: %p",this);    clear();        PoolManager::getInstance()->pop();}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////--------------------------------------------------------------------PoolManager* PoolManager::s_singleInstance = nullptr;PoolManager* PoolManager::getInstance(){    if (s_singleInstance == nullptr)    {        s_singleInstance = new PoolManager();        // Add the first auto release pool        new autoreleasePool("cocos2d autorelease pool");    }    return s_singleInstance;}voID PoolManager::destroyInstance(){    delete s_singleInstance;    s_singleInstance = nullptr;}PoolManager::PoolManager(){    _releasePoolStack.reserve(10);}PoolManager::~PoolManager(){    cclOGINFO("dealLocing PoolManager: %p",this);        while (!_releasePoolStack.empty())    {        autoreleasePool* pool = _releasePoolStack.back();                delete pool;    }}autoreleasePool* PoolManager::getCurrentPool() const{    return _releasePoolStack.back();}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);}voID PoolManager::pop(){    CC_ASSERT(!_releasePoolStack.empty());    _releasePoolStack.pop_back();}NS_CC_END
可以看到
autoreleasePool* PoolManager::getCurrentPool()返回了一个autoreleasePool的指针,<pre name="code" >autoreleasePool这个指针将在Ref autoRelease的时候添加到了一个叫<pre name="code" >_managedobjectArray的vector里面去了。
那么自动管理是怎么做到呢?
转到CCDirector.cpp
<pre name="code" >voID displaylinkDirector::mainLoop(){    if (_purgeDirectorInNextLoop)    {        _purgeDirectorInNextLoop = false;        purgeDirector();    }    else if (! _invalID)    {        drawScene();             // release the objects        PoolManager::getInstance()->getCurrentPool()->clear();    }}
 
 
  
在这个文件的上面那个函数中,我们可以看到每个loop里面在渲染场景之后PoolManager做了一个 *** 作,这个 *** 作就是autoreleasePool类里面的clear,
这个clear就是将维护的_managedobjectArray里面的每个对象执行release *** 作,最后把_managedobjectArray清空。
<span >看到这里我们大约知道了他的原理了。</span>
1、我们在创建Ref的时候计数器值是1;
2、执行retain的时候计数器加1,执行release的时候计数器减1,当计数器为0时由对象自己删除自己。
3、当我们执行autorelease的时候,Ref把释放的 *** 作的权利给了autoreleasePool这个友元类,就是如果我们没有任何retain *** 作,这个Ref的对象将在这一帧结束后被autoreleasePool的clear给释放掉。
4、当我们在创建后又retain,那么将保留对这个Ref对象的控制权,只有在你release后才会被释放,因为autoreleasePool只执行一次便不再参与该对象的内存管理。
<span ></span>
<span >差不多我了解的cocos2dx引用计数的原理就是这么多了,博主的水平也是非常有限,哪里写得不好或出错,欢迎指正交流。</span>
总结

以上是内存溢出为你收集整理的cocos2dx 3.x 源码阅读之一 引用计数原理全部内容,希望文章能够帮你解决cocos2dx 3.x 源码阅读之一 引用计数原理所遇到的程序开发问题。

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

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

原文地址: http://outofmemory.cn/web/1008112.html

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

发表评论

登录后才能评论

评论列表(0条)

保存