cocos2dx源码之 引用计数及自动释放

本文探讨了Cocos2dx中的内存管理,特别是引用计数和自动释放池的机制。C语言使用malloc/free,C++使用new/delete,而在Cocos2dx中,可以通过retain和release进行内存管理。Ref对象的释放通过调用release实现,而AutoreleasePool在clear过程中执行此操作。release主要在detachChild时执行,retain则在insertChild并添加到_children容器时发生。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先让我们从C/C++的内存管理开始说起。

C语言的创建释放内存使用的是 malloc 和free
c++ 中是 new 和delete
在cocos2dx里面,可以使用c和c++的,也可以使用封装过的 retain和release 间接的去管理内存。


void Ref::retain()
{
    CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
    ++_referenceCount; //引用计数++
}

void Ref::release()
{
    CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
    --_referenceCount; //引用计数--
    //中间去掉了无关代码
    if (_referenceCount == 0) //引用计数为0的时候delete this
    {
#if CC_REF_LEAK_DETECTION //内存泄露
        untrackRef(this);
#endif
        delete this;
    }
}

下面我们去Ref类中看看_referenceCount刚创建的时候默认值是多少
Ref::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
}

接下来我们去看加入自动释放池之后的事情。看引擎是怎么处理自动释放的。


#define CREATE_FUNC(__TYPE__) \
static __TYPE__* create() \
{ \
    __TYPE__ *pRet = new(std::nothrow) __TYPE__(); \
    if (pRet && pRet->init()) \
    { \
        pRet->autorelease(); \    //创建成功后第一件事就把对象放入自动释放池。以达到自动管理内存,自动释放指针对应的内存的效果。
        return pRet; \
    } \
    else \
    { \
        delete pRet; \
        pRet = NULL; \
        return NULL; \
    } \
}



跟踪进去就是这样的代码
Ref* Ref::autorelease()
{
    PoolManager::getInstance()->getCurrentPool()->addObject(this);
    return this;
}

一眼就看出,加入自动释放池。

void AutoreleasePool::addObject(Ref* object)
{
    _managedObjectArray.push_back(object);
}

放进_managedObjectArray这个集合里了。

之前说了要想释放一个Ref对象,调用Ref::release就行了。

AutoreleasePool要自动释放肯定会调用这个函数,在这个类中搜一下release(),
果然在AutoreleasePool::clear中找到了。

void AutoreleasePool::clear()
{
    std::vector<Ref*> releasings;
    releasings.swap(_managedObjectArray);//交换 _managedObjectArray 和 releasings
    //交换之后_managedObjectArray会置为空集合,也就是说,每个ref类都只会自动释放一次。也就是初始化的引用计数--变成0。
<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">    //也就解释了为什么刚创建的ref对象,如果不被addchild就会被释放的情况。 下次使用的时候会内存出错。</span>
    for (const auto &obj : releasings)
    {
        obj->release();//一个个释放
    }
}

然后找下哪里调用的这个方法,这个有点难找了。这里可以断点找一下调用的地方。

找到了,是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();//这里是主循环,每一帧都会执行,也解释了为什么叫自动释放。。。
    }
}


addchild 的时候会调用retain,remove的时候会调用release。以达到,remove后自动释放的效果。

具体的代码,可以自己去断点查看或者自己猜猜。

retain大致位置是在 insertChild ->_children.pushBack

release大致是在 detachChild -> _children.erase()中



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值