cocos2d-x 之 内存管理(5)

上一篇文件讲到了CCObject中实现的内存自动管理内存

下面介绍两个很重要的类,一个是内存池类 CCAutoReleasePool ,另一个类是内存池管理类 CCPoolManager 

这两个类结合在一起使用.

先看一下CCAutoReleasePool类,这个类其实就是对 CCArray的一个封装,把需要自动释放的对象,都添加到此类的成员变量m_pManagedObjectArray中

CCAutoReleasePool类的定义如下.还是直接在注释中解释比较方便,直接看注释吧.

 //内存池,其实就是一个CCArray的封装
class CC_DLL CCAutoreleasePool : public CCObject
{

    CCArray*    m_pManagedObjectArray;    
public:
    CCAutoreleasePool(void);
    ~CCAutoreleasePool(void);

    //向内存池添加一个对象
    void addObject(CCObject *pObject);

    //删除内存池中的pObject对象
    void removeObject(CCObject *pObject);

    //清空内存池中所有的对象
    void clear();
};
CCAutoreleasePool 类中主要就3个函数,addObject函数,向内存池中添加一个自动释放的对象的指针. removeObject函数,向内存中把pObject这个对象删除 
最后一个clear函数,把内存池中所有的对象都释放掉
此类需要结合CCPoolManager使用
再看下 CCAutoReleasePool 类的实现文件,看注释,代码如下
CCAutoreleasePool::CCAutoreleasePool(void)
{
    m_pManagedObjectArray = new CCArray();    //新建一个内存池,其实就是一个数组,里面存放的元素都是需要自动释放的内存的对象
    m_pManagedObjectArray->init();            //初始化内存池
}

CCAutoreleasePool::~CCAutoreleasePool(void)
{
    CC_SAFE_DELETE(m_pManagedObjectArray); //释放内存池
}

void CCAutoreleasePool::addObject(CCObject* pObject)
{
    m_pManagedObjectArray->addObject(pObject);//向内存池中添加一个对象pObject,引用计数加 1

    CCAssert(pObject->m_uReference > 1, "reference count should be greater than 1");
    ++(pObject->m_uAutoReleaseCount);    //自动释放内存的标识 ++ ,++后就大于0了,就表明此对象会自动释放内存,具体在哪释放,后面文章会介绍
    pObject->release(); // 因为向内存池中添加对象的时候,引用计数已经加1了,所以再减1
}

//释放对象pObject的内存
void CCAutoreleasePool::removeObject(CCObject* pObject)
{
    //如果对象pObject的自动释放标识大于0,则将对象pObject从内存池中删除掉
    for (unsigned int i = 0; i < pObject->m_uAutoReleaseCount; ++i)
    {
        //将对象pObject从内存池中删除掉
        m_pManagedObjectArray->removeObject(pObject, false);
    }
}

//清理内存池
void CCAutoreleasePool::clear()
{
    //如果内存池中的元素个数大于0
    if(m_pManagedObjectArray->count() > 0)
    {
        //CCAutoreleasePool* pReleasePool;
#ifdef _DEBUG
        int nIndex = m_pManagedObjectArray->count() - 1; //内存池中,也就是数组的最后一个元素的索引
#endif

        //下面是遍历数组,就是上次文章讲到的数组遍历的反序遍历,就是从尾部向头部的顺序进行遍历
        //把内存池中所有的元素的自动释放标识都减1
        CCObject* pObj = NULL;
        CCARRAY_FOREACH_REVERSE(m_pManagedObjectArray, pObj)
        {
            if(!pObj)
                break;

            --(pObj->m_uAutoReleaseCount);    //自动释放内存标识减1
            //(*it)->release();
            //delete (*it);
#ifdef _DEBUG
            nIndex--; //最后一个元素的索引位置也跟着减1
#endif
        }

        //删除内存池中所有的元素
        m_pManagedObjectArray->removeAllObjects();
    }
}

以上是CCAutoReleasePool类的所有源码的解析,通过解析可以看出,CCAutoReleasePool就是封装了一个CCArray的一个对象,用一个数组存放所有的自动释放内存的对象

这个类需要和 CCPoolManager在结合使用,CCPoolManager从名字可以知道,是内存池管理器.专门用来管理内存池的.

CCPoolManager使用一个CCArray来封装了一个栈结构,栈顶为数组的最后一个元素,栈底为第一个元素.栈中所有的元素其实就是一个内存池.

可见CCAutoReleasePool需要和CCPoolManager结合来使用

与上面一样,直接发代码,源码里面都有清楚的注释,先看CCPoolManager的类定义,源码如下

class CC_DLL CCPoolManager
{
    CCArray           *m_pReleasePoolStack;    //
    CCAutoreleasePool *m_pCurReleasePool;       //栈顶元素

    CCAutoreleasePool* getCurReleasePool();    //获得当前的栈顶元素
public:
    CCPoolManager();    //构造函数
    ~CCPoolManager();    //析造函数
    void finalize();    //清理栈内存的工作,栈中每个元素都是一个内存池,此函数将栈中每个内存池的内存都释放掉
    void push();        //压栈
    void pop();            //弹栈,删除最栈顶元素,使以冰月的次栈顶元素成为当前的栈顶元素

    void removeObject(CCObject* pObject);   //删除栈顶元素中对应内存池中的对象pObject
    void addObject(CCObject* pObject);        //向栈顶元素中对应内存中添加对象pObject

    static CCPoolManager* sharedPoolManager();  //单例类,返回单例的实例
    static void purgePoolManager();                //释放单例类


    //将CCAutoreleasePool类声明为CCPoolManager的友元类,使得CCPoolManager成员函数中可以直接访问CCPoolManager中的私有变量
    friend class CCAutoreleasePool;                
};

CCPoolManager使用了一个数组来封装成一个栈.利用栈来管理内存池.

再看CCPoolManager类的实现文件,源码如下:

//--------------------------------------------------------------------
//
// CCPoolManager
//
//--------------------------------------------------------------------
static CCPoolManager* s_pPoolManager = NULL;        //全局的静态变量

//单例类
CCPoolManager* CCPoolManager::sharedPoolManager()
{
    if (s_pPoolManager == NULL)
    {
        s_pPoolManager = new CCPoolManager();
    }
    return s_pPoolManager;
}

//释放单例类
void CCPoolManager::purgePoolManager()
{
    CC_SAFE_DELETE(s_pPoolManager);
}

CCPoolManager::CCPoolManager()
{
    m_pReleasePoolStack = new CCArray();    //新创建一个栈,其实就是用数组来模拟栈的行为,栈中的元素的类型为 CCAutoreleasePool*  
    m_pReleasePoolStack->init();            //初始化一个栈
    m_pCurReleasePool = 0;                    //将当前栈顶的元素置为NULL

}

CCPoolManager::~CCPoolManager()
{
    
     finalize();
 
     // we only release the last autorelease pool here 
    m_pCurReleasePool = 0;
     m_pReleasePoolStack->removeObjectAtIndex(0);
 
     CC_SAFE_DELETE(m_pReleasePoolStack);
}

//作清理栈内存的工作,栈中每个元素都是一个内存池,此函数将栈中每个内存池的内存都释放掉
void CCPoolManager::finalize()
{
    if(m_pReleasePoolStack->count() > 0)
    {
        //CCAutoreleasePool* pReleasePool;
        CCObject* pObj = NULL;
        CCARRAY_FOREACH(m_pReleasePoolStack, pObj)
        {
            if(!pObj)
                break;
            CCAutoreleasePool* pPool = (CCAutoreleasePool*)pObj;
            pPool->clear();
        }
    }
}

//压栈
void CCPoolManager::push()
{
    CCAutoreleasePool* pPool = new CCAutoreleasePool(); //新建一个内存池,此时pPool 的引用计数为 ref = 1
    m_pCurReleasePool = pPool;                            //并把新建的内存池赋值给栈顶,成了为栈顶元素

    m_pReleasePoolStack->addObject(pPool);              //把新建的内存池压入栈中,因为addObject内部作了retain的操作,此时pPool的引用计数为 ref = 2

    pPool->release();                                   //引用计数再减1,是为了回到刚创建时的状态,此时ref = 1
}

//弹栈,把当前的栈顶元素删除,把次栈顶元素变成栈顶元素
void CCPoolManager::pop()
{
    //如果栈顶元素为NULL,则什么也不做,返回
    if (! m_pCurReleasePool)
    {
        return;
    }

    //得到栈中元素的个数
    int nCount = m_pReleasePoolStack->count();

    //把栈顶元素对应的内存池内存释放了
    m_pCurReleasePool->clear();

    //如果栈中元素的个数大于1,就是删除栈顶元素后,栈不为空
    if(nCount > 1)
    {
        //把栈顶元素删除了
        m_pReleasePoolStack->removeObjectAtIndex(nCount-1);

        //把次栈顶元素赋值给当前的栈顶元素
        m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - 2);
    }

}

void CCPoolManager::removeObject(CCObject* pObject)
{
    CCAssert(m_pCurReleasePool, "current auto release pool should not be null");

    //删除栈顶元素中对应内存池中的对象pObject
    m_pCurReleasePool->removeObject(pObject);
}

void CCPoolManager::addObject(CCObject* pObject)
{
    //把对象pObject添加到栈顶元素对应的内存池中
    getCurReleasePool()->addObject(pObject);
}

//得到栈顶元素对应的内存池
CCAutoreleasePool* CCPoolManager::getCurReleasePool()
{
    //如果栈顶元素为NULL,也就是此栈中没有元素
    if(!m_pCurReleasePool)
    {
        //则新建一个元素,并压入栈中,并把此元素赋值给当前栈顶元素
        push();
    }

    CCAssert(m_pCurReleasePool, "current auto release pool should not be null");


    //返回栈顶元素
    return m_pCurReleasePool;

}

通过以上源码的分析可以看到:cocos2d-x是通过CCAutoreleasePool来保存所有需要自动释放内存的对象.而通过CCPoolManager来管理内存池的.

那么对象的释放到底是在什么时候释放的? 下篇文章介绍对象释放的时机




原文地址:https://www.cnblogs.com/start1225/p/3531912.html