zoukankan      html  css  js  c++  java
  • 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来管理内存池的.

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




  • 相关阅读:
    在SQL中删除重复记录
    SQL语句生成SQLServer数据字典
    还原数据库时出现的问题
    Active Server Pages 错误 'ASP 0201'
    VS.NET新建项目时出现“两者需要映射到相同的服务器位置”
    cs0016: 未能写入输出文件“c:\windows\microsoft.net\***.dll”错误处理
    Svchost.exe进程详解
    5y5.us
    C# 中返回星期的函数
    怎样用C#实现完整文档打印功能
  • 原文地址:https://www.cnblogs.com/start1225/p/3531912.html
Copyright © 2011-2022 走看看