美文网首页Cocos2d-X与游戏开发
cocos2d-x源码中的内存管理

cocos2d-x源码中的内存管理

作者: 二号猎人 | 来源:发表于2018-08-03 20:38 被阅读3次

cocos2d-x的内存管理原则——引用计数机制

内存管理:引用计数机制, 当一个对象执行retain操作的时候,引用计数加1,当执行release操作的时候,引用计数减1,当引用计数为0时,此对象被释放。

通过基类Ref的代码实现,便可以明白基本的内存管理机制,直接打开 CCRef.cpp文件

//  我们直接来看看CCRef.cpp文件
Ref::Ref()
: _referenceCount(1)  //注:当Ref对象被创建,此对象的引用计数为1

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

void Ref::release()   //release方法
{
    CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
    --_referenceCount;  //引用计数减1

    if (_referenceCount == 0) //当引用计数为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

        delete this; //删除对象!!!
    }
}

Ref* Ref::autorelease()  //autorelease方法
{
    PoolManager::getInstance()->getCurrentPool()->addObject(this);   //将对象加入到自动释放池中
    return this;
}

unsigned int Ref::getReferenceCount() const
{
    return _referenceCount;  //获取引用计数个数
}

通过看Ref类的源码,可以得出结论:

1.当一个对象被创建的时候,将此对象的引用计数设置为1
2.当执行一次retain,引用计数加1,
3.执行一次release,引用计数减1
4.当引用计数为0的时候,此对象被释放delete

Ref的autorelease方法

Ref* Ref::autorelease()  //autorelease方法
{
    PoolManager::getInstance()->getCurrentPool()->addObject(this);   //将对象加入到自动释放池中
    return this;
}

什么时候调用autorelease方法?

    Label* Label::create()
    {
        auto ret = new (std::nothrow) Label;
    
        if (ret)
        {
            ret->autorelease();  //create函数里,会自动执行autorelease方法
        }
    
        return ret;
    }

可以看出,在create函数里,会自动执行autorelease方法, 也就是加入到自动释放池

AutoreleasePool 自动释放池:

它有个属性_managedObjectArray,可以将执行了autorelease方法的Ref对象加入_managedObjectArray,当AutoreleasePool::clear()时,也就是对_managedObjectArray中的每一项执行release操作

PoolManager 自动释放池管理类

有个生成器属性_releasePoolStack,里面加入的都是AutoreleasePool的对象,可以获取当前的AutoreleasePool

可以看到实现是建立了一个PoolManager的单例,接下来看看PoolManager,有个生成器属性_releasePoolStack

PoolManager* PoolManager::s_singleInstance = nullptr;

PoolManager* PoolManager::getInstance()
{  //PoolManager是个单例
    if (s_singleInstance == nullptr)
    {
        s_singleInstance = new (std::nothrow) PoolManager();
        // Add the first auto release pool
        new AutoreleasePool("cocos2d autorelease pool");
    }
    return s_singleInstance;
}
AutoreleasePool* PoolManager::getCurrentPool() const
{
    return _releasePoolStack.back(); //_releasePoolStack最后的一个AutoreleasePool对象
}

//注意接下来的两个函数:push,pop
void PoolManager::push(AutoreleasePool *pool)
{
    _releasePoolStack.push_back(pool); //在_releasePoolStack这个vector尾部加入pool
}

void PoolManager::pop()
{
    CC_ASSERT(!_releasePoolStack.empty());
    _releasePoolStack.pop_back();//删除_releasePoolStack最后一个元素
}

PoolManager::getInstance()->getCurrentPool()->addObject(this)中的getCurrentPool可以看出获取到了一个AutoreleasePool对象,然后我们查看AutoreleasePool

class CC_DLL AutoreleasePool
{   
private:
    std::vector<Ref*> _managedObjectArray;   //AutoreleasePool有个生成器属性_managedObjectArray,用来装Ref对象
};

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

void AutoreleasePool::clear() //清楚操作
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
    _isClearing = true;
#endif
    std::vector<Ref*> releasings;
    releasings.swap(_managedObjectArray);  //遍历_managedObjectArray,为每一个对象执行release操作
    for (const auto &obj : releasings)
    {
        obj->release();
    }
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
    _isClearing = false;
#endif
}

PoolManager::getInstance()->getCurrentPool()->addObject(this)中的addObject其实就是给_managedObjectArray这个vector中添加一个Ref对象

可以看到AutoreleasePool::clear()的方法实现,其实是把_managedObjectArray里面的每一个对象执行一次release操作,clear函数会在DisplayLinkDirector的mainLoop函数中被调用,一帧调用一次,及时回收。

void DisplayLinkDirector::mainLoop()
{
    if (_purgeDirectorInNextLoop)
    {
        _purgeDirectorInNextLoop = false;
        purgeDirector();
    }
    else if (_restartDirectorInNextLoop)
    {
        _restartDirectorInNextLoop = false;
        restartDirector();
    }
    else if (! _invalid)
    {
        drawScene();
     
        // release the objects !!!在此处执行clear操作
        PoolManager::getInstance()->getCurrentPool()->clear();
    }
}

总结一下就是内存管理的源代码实现:

常见的label,button等对象在执行create()函数的时候,会自动执行autorelease()方法,就是将对象添加到AutoreleasePool的_managedObjectArray中,而在游戏中每一帧都会调用通过PoolManager获取到当前的AutoreleasePool对象,然后执行AutoreleasePool::clear()方法,给_managedObjectArray中的每一个对象执行release操作。

相关文章

  • cocos2d-x源码中的内存管理

    cocos2d-x的内存管理原则——引用计数机制 内存管理:引用计数机制, 当一个对象执行retain操作的时候,...

  • 1.1.01_COCOS2D-X内存管理机制

    Cocos2d-x 的内存管理方法基于 Cocos2d-iPhone 的 Objective-C 风格的内存管理,...

  • STL内存管理详细分析

    STL中内存管理非常精妙,本文以SGI STL为例,分析其内存管理的设计思路,也是对侯捷老师的《STL源码剖析》中...

  • iOS - 老生常谈内存管理(四):内存管理方法源码分析

    建议去掘金查看本文,带目录方便阅读。《iOS - 老生常谈内存管理(四):内存管理方法源码分析》 走进苹果源码分析...

  • 从源码角度看Golang的堆内存管理

    从源码角度看Golang的堆内存管理 本章主要从源码角度针对Go堆上的内存管理进行分析。仅关注linux系统下的逻...

  • go内存管理

    这篇文章可以看作是内存管理这篇长文的学习总结吧,原文基于源码剖析了整个go的内存管理,非常详尽。 程序中的数据和变...

  • Netty源码分析

    最近完成了Netty源码分析,希望可以和大家共同交流 Netty Recycler源码解析Netty 内存管理源码...

  • 散列表

    散列表的内存管理方案,在源码中是通过SideTables()结构来实现的, SideTables()中有很多Sid...

  • Spark源码分析十三-内存管理模型

    在分析spark内存管理源码之前,先来了解下,统一内存模型原理性的知识,spark在3.0版本移除了静态内存管理的...

  • cocos2d-x3.0的内存管理机制

    cocos2d-x中的内存管理机制可以看成给予智能指针的一个变体,但它同时是程序员进而一项实用垃圾回收机制那样不需...

网友评论

    本文标题:cocos2d-x源码中的内存管理

    本文链接:https://www.haomeiwen.com/subject/uaiuvftx.html