从智能指针联想到cocos2dx内存管理机制

1. 写在前面

C++在C11之前,都是使用new分配内存delete释放内存。这看起来是不是非常轻松?其实这会暴露出许多缺点

  1. 内存重复释放
  2. 野指针:指向的内存已经被释放了,但是指针还在使用
  3. 内存泄露:不再使用的内存没有释放,内存占用率过高

C11就引出了智能指针来解决以上的三个问题。
我已经有博客提到智能指针了,想多了解的朋友可以去看看:C++多线程下的shared_ptr、C11新特性之智能指针。
shared_ptr:多个智能指针可共享同一内存,使用引用计数
unique_ptr:不能和其他智能指针指向同一个内存,直接赋值给其他指针会报错,但是可以使用move来转移,转移后之前的智能指针就失效。
week_ptr:使用lock()函数,它可以检测weak_ptr访问的对象是否存在

既然智能指针看起来解放了大家去释放内存的步骤,那么为什么在cocos里面我们不使用智能指针来管理内存呢?

  1. 性能损失
  2. 仍要显示声明指针

2. Ref

cocos2dx虽然没有使用智能指针,但是它用了一个与智能指针非常相似的东西,也就是下面我们要说的Ref。
Ref的主要流程
当对象被创建时候,引用计数为1。为了保证对象的存在,可以调用retain函数保持对象,retain会使其引用计数加1,如果不需要这个对象可以调用release函数,release使其引用计数减1。当对象的引用计数为0的时候,引擎就知道不再需要这个对象了,就会释放对象内存。

class Ref:
    def retain():pass 增加引用
    def release():pass 减少引用
    def autorelease():pass 交给自动释放池
    def getRefreenceCount():pass
    self._referenceCount = 0
    #friend class AutoreleasePool

在使用Node节点对象时候,addChild函数可以保持Node节点对象,使引用计数加1。通过removeChild函数移除Node节点对象,使引用计数减1。

2.1 Ref::retain()

retain()对其引用计数加1

void Ref::retain()  
{  
    CCASSERT(_referenceCount > 0, "reference count should be greater than 0");  
    ++_referenceCount;  
}  
2.2 Ref::release()

release()先对引用计数-1,然后判断引用计数是否为0,若为0则删除对象

void Ref::release()  
{  
    CCASSERT(_referenceCount > 0, "reference count should be greater than 0");  
    对其引用计数值进行-1  
    --_referenceCount;  
    引用计数为0,删除对象  
    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  
        从保存Ref*的list中删除  
        #if CC_REF_LEAK_DETECTION  
            untrackRef(this);  
        #endif  
        删除该对象  
        delete this;  
    }  
}  
2.3 Ref::autorelease()

autorelease()将节点添加到自动释放池中,它由PoolManager进行管理。
调用内存管理PoolManager的单例对象中存储的自动释放池对象的addObject函数加入当前的Ref实例对象的指针
cocos2dx为了不用显式调用AutoRelease,一般让程序员通过create来获取对象,而create里帮我们调用好了AutoRelease。autorelease主要应用在ui元素上。

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

3. CCAutoreleasePool

这个类主要含有下面几个方法

void addObject(Ref *object); 将Ref添加到自动释放池  
void clear(); 清除自动释放池所有对象  
bool contains(Ref* object) const; 检测自动释放池中是否有Object对象  
void dump(); 打印删除的对象的地址  
3.1 构造函数

构造函数里可以看出,每一个autoreleasePool对象都有一个管理obj的队列managedObjectArray用于存放obj,并且所有Pool对象都是放在poolManage的单例对象的堆中

AutoreleasePool::AutoreleasePool()  
    : _name("")
    #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)  
        , _isClearing(false)
    #endif  
    {   容器扩容,申请保存ref的vector的size增加150  
        _managedObjectArray.reserve(150);  
        把AutoreleasePool添加到管理类生明的vector中  
        PoolManager::getInstance()->push(this);  
    } 
3.2 析构函数

析构函数是先清除自动释放池所有obj对象,然后再将自己从poolManage的堆中弹出

AutoreleasePool::~AutoreleasePool()  
{  
    CCLOGINFO("deallocing AutoreleasePool: %p", this);  
    clear();  
    PoolManager::getInstance()->pop();  
}  
3.3 addObject

把object添加到managedObjectArray自动释放池

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

clear就是将managedObjectArray里的所有obj都执行release

void AutoreleasePool::clear()  
{  
    #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)  
        _isClearing = true;
    #endif  
    std::vector releasings;  
    releasings.swap(_managedObjectArray);  
   //遍历自动释放池managedObjectArray里存放的所有的Ref  
    for (const auto &obj : releasings)  
    {  
        //调用obj的release(),对obj的引用计数-1(如果对象引用计数为0则删除)  
        obj->release();  
    }  
    #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)  
        _isClearing = false;
    #endif  
}  

4. PoolManager

  1. PoolManager类对象是一个单例对象singleInstance ,整个工程中只有一个
  2. PoolManager类维护着一个堆releasePoolStack,用来存放工程中所有的AutoreleasePool对象;
  3. 名字叫"cocos2d autorelease pool"的自动释放池对象保存工程中大部分渲染的节点以及我们通过create()创建的对象,这个AutoreleasePool对象是工程刚启动时就生成的;
4.1 顺便通过PoolManager来重新学一下
static PoolManager* s_singleInstance; //poolmanager的实例,再强调一遍这是个单例对象  

PoolManager* PoolManager::s_singleInstance = nullptr; 

PoolManager* PoolManager::getInstance()  
{  
    //判断是否存在singleInstance   
    if (s_singleInstance == nullptr)  
    {  
        //如果singleInstance 为nullptr,new一个PoolManager单例  
        s_singleInstance = new (std::nothrow) PoolManager();  
        //new一个AutoreleasePool并取名字为"cocos2d autorelease pool"  
        new AutoreleasePool("cocos2d autorelease pool");  
    }  
    return s_singleInstance;  
}  

void PoolManager::destroyInstance()  
{  
    delete s_singleInstance;  
    s_singleInstance = nullptr;  
}  

你可能感兴趣的:(从智能指针联想到cocos2dx内存管理机制)