游戏客户端之内存管理(cocos2d-x 引用计数)

 

cocos2dx 里的使用引用计数的对象都是继承CCObject的。

引擎里大多数提供的类型是使用引用计数的。

可以手动release 和retain,分别是计数减和加

如果不想手动释放就调用autorelease,该对象会被放到CCPoolManager 内存管理池的当前的释放池里面(内存管理池里有内存池数组,最后创建的内存池就是当前的内存池)。

内存管理器是个单例(CCPoolManager::sharedPoolManager()),可以在程序结束时删除。


刚创建的对象(引用为1)添加到内存池管理器后,引用计数还是1(先加1后减1),会在当前帧渲染后删除,如果手动retain一次,引用计数就是2,当前帧过后引用计数减1就成了1,以后需要手动释放。


本文内容:

 1、引用计数对象

2、内存池管理器

3、管理器的对象回收

(1)应用的消息循环

(2) 导演者的主循环

(3)回收内存管理器中的当前内存池

 


1、引用计数对象

在构造函数时为1 ,m_bManaged 的标记为false

CCObject::CCObject(void)
{
    static unsigned int uObjectCount = 0;

    m_uID = ++uObjectCount;
    m_nLuaID = 0;

    // when the object is created, the refrence count of it is 1
    m_uReference = 1;
    m_bManaged = false;//对于刚创建的CCObject对象,是不被释放池管理的
}

析构的时候,若是被管理的就由内存管理者移除

CCObject::~CCObject(void)
{
    // if the object is managed, we should remove it
    // from pool manager
    if (m_bManaged)
    {
        CCPoolManager::sharedPoolManager()->removeObject(this);
    }


    // if the object is referenced by Lua engine, remove it
    if (m_nLuaID)
    {
        CCScriptEngineManager::sharedManager()->getScriptEngine()->removeCCObjectByID(m_nLuaID);
    }
}

函数release 和retain就是计数减和加

void CCObject::release(void)
{
    CCAssert(m_uReference > 0, "reference count should greater than 0");
    --m_uReference;


    if (m_uReference == 0)
    {
        delete this;
    }
}

void CCObject::retain(void)
{
    CCAssert(m_uReference > 0, "reference count should greater than 0");

    ++m_uReference;
}

函数autorelease()就是把自己交由内存池管理者来管理

CCObject* CCObject::autorelease(void)
{
    CCPoolManager::sharedPoolManager()->addObject(this);

    m_bManaged = true;
    return this;
}

2、内存池管理器

内存池管理者有一个释放池队列(数组形式的),操作如下

void CCPoolManager::addObject(CCObject* pObject)
{
    getCurReleasePool()->addObject(pObject);
}

CCAutoreleasePool* CCPoolManager::getCurReleasePool()
{
    if(!m_pCurReleasePool)
    {
        push();
    }

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

void CCPoolManager::push()
{
    CCAutoreleasePool* pPool = new CCAutoreleasePool();       //ref = 1
    m_pCurReleasePool = pPool;

    m_pReleasePoolStack->addObject(pPool);                   //ref = 2

    pPool->release();                                       //ref = 1
}


一个释放池里面有被管理的实体的列表(数组形式)

class CC_DLL CCAutoreleasePool : public CCObject
{
    CCArray*    m_pManagedObjectArray;   

}


实体(CCObject)实际上是存放在m_pManagedObjectArray

内存管理者析构的时候就删除释放池里面的实体队列 以及 释放池 堆栈

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)
    {
        CCObject* pObj = NULL;
        CCARRAY_FOREACH(m_pReleasePoolStack, pObj)
        {
            if(!pObj)
                break;
            CCAutoreleasePool* pPool = (CCAutoreleasePool*)pObj;
            pPool->clear();//release 所有管理的引用计数对象
        }
    }
}

在内存池管理者构造函数里面初始化 释放池数组

CCPoolManager::CCPoolManager()
{
    m_pReleasePoolStack = new CCArray();    
    m_pReleasePoolStack->init();//这里在是多写了的,CCArray默认构造函数里面就有init();   
    m_pCurReleasePool = 0;
}


CCArray::CCArray()
: data(NULL)
{
    init();
}

 释放池的内存数组初始化大小为1

bool CCArray::init()
{
    return initWithCapacity(1);
}

 CCArray::initWithCapacity(unsigned int capacity)
{
    ccArrayFree(data);
    data = ccArrayNew(capacity);
    return true;
}

ccArray* ccArrayNew(unsigned int capacity) 
{
if (capacity == 0)
capacity = 1;

ccArray *arr = (ccArray*)malloc( sizeof(ccArray) );
arr->num = 0;
arr->arr =  (CCObject**)calloc(capacity, sizeof(CCObject*));
arr->max = capacity;

return arr;
}

其中ccArray* data;

ccArray 类型定义

typedef struct _ccArray {
unsigned int num, max;
CCObject** arr;
} ccArray;


3、管理器的对象回收

回收内存池管理者的当前内存池的引用计数对象

(1)应用的消息循环

int CCApplication::run()

{
  ......
    while (1)
    {

//没有消息才处理帧渲染(在导演者循环里的,画当前场景上的所有图形节点 ,和释放内存管理器的当前内存池的引用计数对象
if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            // Get current time tick.
            QueryPerformanceCounter(&nNow);

            // If it's the time to draw next frame, draw it, else sleep a while.
            if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart)
            {
                nLast.QuadPart = nNow.QuadPart;
                CCDirector::sharedDirector()->mainLoop();
            }
            else
            {
                Sleep(0);
            }
            continue;
        }
//有事件则优先处理程序窗口事件
        if (WM_QUIT == msg.message)
        {
            // Quit message loop.
            break;
        }

        // Deal with windows message.
        if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }


    return (int) msg.wParam;
}


(2)导演者的主循环

 CCDisplayLinkDirector::mainLoop(void)
{
    if (m_bPurgeDirecotorInNextLoop)
    {
        m_bPurgeDirecotorInNextLoop = false;
        purgeDirector();
    }
    else if (! m_bInvalid)
     {
         drawScene(); //渲染当前场景
     
         // release the objects
         CCPoolManager::sharedPoolManager()->pop();        //释放当前内存池的引用计数对象
     }
}

(3)回收内存管理器中的当前内存池

void CCPoolManager::pop()
{
    if (! m_pCurReleasePool)
    {
        return;
    }

     int nCount = m_pReleasePoolStack->count();

    m_pCurReleasePool->clear();//释放当前内存池的引用计数对象
 
      if(nCount > 1)
      {

//去掉最后一个内存池
        m_pReleasePoolStack->removeObjectAtIndex(nCount-1);

//取出倒数最后一个内存池作为当前的内存池
        m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - 2);
    }
}



3、管理器的对象回收

(1)在导演者的主循环里回收所有

3、管理器的对象回收

回收内存池管理者的当前内存池的引用计数对象

(1)应用的消息循环

你可能感兴趣的:(游戏编程)