Cocos2d-x学习笔记(十四)CCAutoreleasePool详解

原创文章,转载请注明出处:http://blog.csdn.net/sfh366958228/article/details/38964637

前言

之前学了那么多的内容,几乎所有的控件都要涉及内存管理类CCAutoreleasePool,所以这一次的学习笔记,我们一起来看看CCAutoreleasePool,先从CCObject的autorelease方法入手。

CCObject::autorelease

CCObject* CCObject::autorelease(void)
{
    // 将当前对象添加到内存池中
    CCPoolManager::sharedPoolManager()->addObject(this);

    // 将当前对象设置为已被内存池管理
    m_bManaged = true;
    return this;
}

autoRelease类并不复杂,由代码中的注释可以看出主要还是CCPoolManager的操作,也就是CCAutoreleasePool。

CCAutoreleasePool.h

class CC_DLL CCAutoreleasePool : public CCObject
{
    // 保存所有添加到释放池中的对象
    // 注意CCArray内部是通过将对象retain 然后存储起来的,应用计数会增加1
    CCArray* m_pManagedObjectArray;
public:
    CCAutoreleasePool(void);
    ~CCAutoreleasePool(void);

    // 将对象添加到自动释放池
    void addObject(CCObject *pObject);

    // 将对象从自动释放池中移除
    void removeObject(CCObject *pObject);

    // 将自动释放池中的对象释放掉
    void clear();
};

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);

    // 将对象冲当前的自动释放池中移除
    void addObject(CCObject* pObject);

    // 获取自动释放池管理者,整个程序代码中所有需要使用CCPoolManager的地方都通过这个函数获取
    // 外部不应该自己new一个内存管理者来使用
    static CCPoolManager* sharedPoolManager();

    // 清理当前内存管理者,释放其中的自动释放池以及自动释放池中的所有对象
    static void purgePoolManager();

    friend class CCAutoreleasePool;
};

CCAutoreleasePool.cpp

// 静态成员变量,保存整个程序使用的自动释放池 管理者
static CCPoolManager* s_pPoolManager = NULL;

CCAutoreleasePool::CCAutoreleasePool(void)
{
    //初始化队列,用以保存添加到池中的对象
    m_pManagedObjectArray = new CCArray();
    m_pManagedObjectArray->init();
}

CCAutoreleasePool::~CCAutoreleasePool(void)
{
    CC_SAFE_DELETE(m_pManagedObjectArray);
}

// 将对象添加到自动释放池中
void CCAutoreleasePool::addObject(CCObject* pObject)
{
    // 将对象retain 保存到队列中
    m_pManagedObjectArray->addObject(pObject);

    // 到这里,对象的应用计数最少应该是2
    CCAssert(pObject->m_uReference > 1, "reference count should be greater than 1");

    // 调用对象的release方法,将引用计数减1
    // release方法在CCObject中实现,引用计数减1之后检查引用计数如果为0,则直接删除此对象
    pObject->relearese(); // no ref count, in this case autorelease pool added.
}

void CCAutoreleasePool::removeObject(CCObject* pObject)
{
    // 从自动释放池中移除对象,,使该对象不被自动释放池管理,当然也不会自动释放
    // 第二个参数为false,,因为addObject被没有使引用计数增加,所有这里也不能使应用计数有变化
    m_pManagedObjectArray->removeObject(pObject, false);
}

// 清空自动释放池,释放池内所有对象
void CCAutoreleasePool::clear()
{
    if(m_pManagedObjectArray->count() > 0)
    {
        //CCAutoreleasePool* pReleasePool;
#ifdef _DEBUG
        int nIndex = m_pManagedObjectArray->count() - 1;
#endif

        CCObject* pObj = NULL;
        CCARRAY_FOREACH_REVERSE(m_pManagedObjectArray, pObj)
        {
            if(!pObj)
                break;

            pObj->m_bManaged = false;
            //(*it)->release();
            //delete (*it);
#ifdef _DEBUG
            nIndex--;
#endif
        }
        // 移除所有的在队列中的对象,依次调用所有对象的release方法
        m_pManagedObjectArray->removeAllObjects();
    }
}

//--------------------------------------------------------------------
//
// CCPoolManager
//
//--------------------------------------------------------------------

// 获取自动释放池管理者
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();
    m_pReleasePoolStack->init();

    // 当前的自动释放池为空
    m_pCurReleasePool = 0;
}

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();       //ref = 1
    m_pCurReleasePool = pPool;

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

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

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);
    }
}

void CCPoolManager::removeObject(CCObject* pObject)
{
    // 从当前的自动释放池中移除对象
    CCAssert(m_pCurReleasePool, "current auto release pool should not be null");

    m_pCurReleasePool->removeObject(pObject);
}

void CCPoolManager::addObject(CCObject* pObject)
{
    // 将对象添加到当前的自动释放池中
    getCurReleasePool()->addObject(pObject);
}

CCAutoreleasePool* CCPoolManager::getCurReleasePool()
{

    // 获取当前的自动释放池
    // 如果当前的自动释放池为空,说明自动释放池队列中也为空
    // 通过push方法,添加新的自动释放池
    if(!m_pCurReleasePool)
    {
        push();
    }

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

    return m_pCurReleasePool;
}

自动释放池管理者通过pop方法,将当前自动释放池中的所有对象调用release方法进行释放,pop方法是什么时候在什么地方进行调用的呢?其实答案就在我们之前看到过的mainloop方法中:

void CCDisplayLinkDirector::mainLoop(void)
{
    if (m_bPurgeDirecotorInNextLoop)
    {
        m_bPurgeDirecotorInNextLoop = false;
        purgeDirector();
    }
    else if (! m_bInvalid)
     {
         // 绘制场景
         drawScene();

         // 释放自动释放池
         CCPoolManager::sharedPoolManager()->pop();
     }
}

在每一帧绘制完成之后,当前的自动释放池将会被清理,所有调用了autorelease操作的对象都会被调用release方法,减少其引用计数。如果我们创建的对象调用了autorelease,那么在稍后帧绘制之后,自动释放池被清理的时候此对象的引用计数将被减1,此对象如果没有在其他地方被

retain,那么它将会被释放掉。

在对象的使用上,为保证对象能被正确的释放,需要时刻知道此对象的引用计数为多少,但是很多时候能做到这点很难,除非对所有的接口都很了解,知道其中是否对当前对象做了retain操作或者release操作,如果做不到这点,可以按照cocos2d-x框架中这样的原则去做

1、作为参数传进来的对象,如果你要长期的使用或者管理它,请 retain,不用的时候记得release

2、作为参数传进来的对象,他不是你创建或者retain的,如果你不确定他从哪里来,外面是否知道你会release掉他,请别随便调用release

3、如果你撇开自动释放池,new了一个对象而不调用autorelease,在不使用的时候直接将对象delete掉,这样的做法是很不安全的,除非你创建的对象真的只有你在使用而没有被其他对象retain。但并非代表你不能自己new和delete管理对象,因时而异。

4、创建一个新的对象,调用了对象的autorelease方法,如果想长期的使用他,请使用retain方法(包括addChild到自身,addObject到某个CCArray中),清除时使用release方法(removeChild、CCArray的removeObject等)

时间: 2024-10-03 16:20:21

Cocos2d-x学习笔记(十四)CCAutoreleasePool详解的相关文章

Swift学习笔记十四:构造(Initialization)

类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值.存储型属性的值不能处于一个未知的状态. 你可以在构造器中为存储型属性赋初值,也可以在定义属性时为其设置默认值.以下章节将详细介绍这两种方法. 注意: 当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观测器(property observers). 一.基本语法 class Human{ var name :String init(){ name = "human" } init(n

laravel3学习笔记(十四)

原作者博客:ieqi.net ==================================================================================================== 运行时配置 在 Laravel3 中很多地方我们都可以看到“约定大于配置”的影子,我本人也很喜欢这种工程哲学尤其是在框架领域,当然这并不能代替所有的配置.我们知道 Laravel3 中,主要配置都写在 application/config 文件夹下,在应用逻辑中,往往

hadoop 学习笔记:mapreduce框架详解

hadoop 学习笔记:mapreduce框架详解 开始聊mapreduce,mapreduce是hadoop的计算框架,我 学hadoop是从hive开始入手,再到hdfs,当我学习hdfs时候,就感觉到hdfs和mapreduce关系的紧密.这个可能是我做技术研究的 思路有关,我开始学习某一套技术总是想着这套技术到底能干什么,只有当我真正理解了这套技术解决了什么问题时候,我后续的学习就能逐步的加快,而学习 hdfs时候我就发现,要理解hadoop框架的意义,hdfs和mapreduce是密不

C#学习笔记二: C#类型详解

前言 这次分享的主要内容有五个, 分别是值类型和引用类型, 装箱与拆箱,常量与变量,运算符重载,static字段和static构造函数. 后期的分享会针对于C#2.0 3.0 4.0 等新特性进行. 再会有三篇博客  这个系列的就会结束了. 也算是自己对园子中@Learning Hard出版的<<C#学习笔记>>的一个总结了. 博客内容基本上都是白天抽空在公司写好的了, 但是由于公司内部网络不能登录博客园所以只能够夜晚拿回来修改,  写的不好或者不对的地方也请各位大神指出. 在下感

IOS学习笔记——ViewController生命周期详解

在我之前的学习笔记中讨论过ViewController,过了这么久,对它也有了新的认识和体会,ViewController是我们在开发过程中碰到最多的朋友,今天就来好好认识一下它.ViewController是IOS开发中MVC模式中的C,ViewController是view的controller,ViewController的职责主要包括管理内部各个view的加载显示和卸载,同时负责与其他ViewController的通信和协调.在IOS中,有两类ViewController,一类是显示内容

十四、详解事务

本篇内容 什么是事务,它有什么用? 事务的几个特性 事务常见操作指令详解 事务的隔离级别详解 脏读.不可重复读.可重复读.幻读详解 演示各种隔离级别产生的现象 关于隔离级别的选择 一.什么是事务? 数据库中的事务是指对数据库执行一批操作,这些操作最终要么全部执行成功,要么全部失败,不会存在部分成功的情况. 二.事务的几个特性(ACID) 原子性(Atomicity) 事务的整个过程如原子操作一样,最终要么全部成功,或者全部失败,这个原子性是从最终结果来看的,从最终结果来看这个过程是不可分割的.

Linux学习笔记——用户及权限详解

用户及权限详解    用户.组.权限 安全上下文(secure context): 权限: r   w   x  文件: r:可读,可以使用类似cat等命令查看文件内容: w:可写,可以编辑或删除此文件: X:可执行,exacutable,可以命令提示符下当作命令提交给内核运行:  目录: r:可以对此目录执行ls以列出内部的所有文件: w:可以在此目录创建文件: x:可以使用cd切换进此目录,也可以使用ls -l查看内部文件的详细信息: rwx: r--:只读 r-x:读和执行 ---:无权限

CSS学习笔记(9)--详解CSS中:nth-child的用法

详解CSS中:nth-child的用法 前端的哥们想必都接触过css中一个神奇的玩意,可以轻松选取你想要的标签并给与修改添加样式,是不是很给力,它就是":nth-child". 下面我将用几个典型的实例来给大家讲解:nth-child的实际用途: Tips:还用低版本的IE浏览器的哥们请绕过! :nth-child(2)选取第几个标签,"2可以是你想要的数字" .demo01 li:nth-child(2){background:#090} :nth-child(n

Linux学习笔记—— 用户管理命令详解

 用户管理命令详解  用户管理: useradd,userdel,usermod,passwd,chsh,chfn,finger,id,chage 添加用户: useradd [options] USERNAME    -u (UID)   手动指定UID -g (GID)  (基本组) -G, ...     (附加组) 可以有多个,彼此之间用,号隔开 -c "COMMENT"         注释信息     -d /path/to/somedirectory  指定家目录 -s

[学习笔记]struts.xml配置详解(所有基本配置,包括通配符)

1.<include> 利用include标签,可以将一个struts.xml配置文件分割成多个配置文件,然后在struts.xml中使用<include>标签引入其他配置文件.比如一个网上购物程序,可以把用户配置.商品配置.订单配置分别放在3个配置文件user.xml.goods.xml和order.xml中,然后在struts.xml中将这3个配置文件引入: struts.xml: [html] view plaincopy <?xml version="1.0