cocos内存管理之PoolManager::getInstance()->getCurrentPool()->clear();

前段时间在捕鱼的地方使用对象池的时候,因为对cocos里面的autorelease机制不熟悉,导致了内存的泄露,特写次文章记录一下。

cocos2dx-3.8中的自动内存管理机制是借助引用计数来实现的。对于老版本的cocos引用计数使用的是CCObject,但是这个类在后面就被弃用了,使用Ref来代替,cocos内几乎所有的类都是继承自Ref

Ref基本的原理就是其内部存在一个引用计数_referenceCount,当这个引用计数为0的时候,就会被释放。引用计数通过retainrelease来操作。

Ref从创建到销毁的过程

我就简单的往屏幕中添加一个Label来测试Ref的创建和销毁,首先创建一个Label

auto label = Label::create();
label->setString("KaMi");
addChild(label)

以上代码就是简单的在屏幕中添加一个label,让我们看看create干了什么

Label* Label::create(){
    auto ret = new (std::nothrow) Label();
    if (ret){
        ret->autorelease();
    }
    return ret;
}

create函数是一个工厂方法,cocos中很多类都实现了这个方法,其中可以看到ret->autorelease();,这个函数就是把当前对象加入到自动释放池内,对于自动释放池下面会详细讲解。
注:Ref初始化的时候引用计数为1不是0

接下来看一下addChild(label)里面,这个方法是属于Node的,里面跳转的代码有些长,我就截取了一小部分。

void Node::addChild(Node *child){
    CCASSERT( child != nullptr, "Argument must be non-nil");
    this->addChild(child, child->_localZOrder, child->_name);
}

void Node::addChild(Node* child, int localZOrder, const std::string &name){
    ...
    addChildHelper(child, localZOrder, INVALID_TAG, name, false);
}

void Node::addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag){
    ...
    this->insertChild(child, localZOrder);
    ...
}

void Node::insertChild(Node* child, int z){
    _transformUpdated = true;
    _reorderChildDirty = true;
    _children.pushBack(child);
    child->_localZOrder = z;
}

最终跳转到了insertChild中,通过_children.pushBack(child)label加入到_children中去。那么问题来了,到底在哪里对引用计数+1操作了呢?答案就在pushBack中,_childrencocosRef量身定制的向量Vector<T>,这个向量只能给继承了Ref的类来使用。

void pushBack(T object){
    ...
    _data.push_back( object );
    object->retain();
}

代码中可以看到object->retain(),对添加进来的对象引用+1操作。那么什么时候-1呢?

当我们场景移除的时候,应该是要释放在场景中的label的。Node被移除时会调用当前Node的父亲的removeChild函数,此函数最后会调用Nodecleanup函数,cleanup函数是递归函数,会遍历所有的子节点。当cleanup完之后会从父节点的_children这个向量中删除,此时就会调用release函数。

//当某个儿子节点cleanup完之后会调用_children.earse(childIndex)
iterator erase(ssize_t index){
    ...
    auto it = std::next( begin(), index );
    (*it)->release();
    return _data.erase(it);
}

release函数就是对当前实例的引用计数-1,如果-1后为0那么释放内存。

void Ref::release(){
    ...
    --_referenceCount;

    if (_referenceCount == 0){
        ...
        delete this;
    }
}

自动释放池

Ref中有autorelease函数,咋一看感觉内存不需要我来管了,他会自动释放。然而这个自动和我脑子里想的自动相差一个孙悟空的跟头,毕竟是c++不是java。先看看autorelease的源码

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

代码很短,autorelease并没有release,而是把对象加入到了对象池中。那么这个对象池是什么时候去release里面的对象的呢?接下来就要看DirectormainLoop函数了,这个函数在Director中没有实现,是在DisplayLinkDirector中实现的。

void DisplayLinkDirector::mainLoop(){
    if (_purgeDirectorInNextLoop){
        ...
    }
    else if (_restartDirectorInNextLoop){
        ...
    }
    else if (! _invalid){
        drawScene();

        // release the objects
        PoolManager::getInstance()->getCurrentPool()->clear();
    }
}

mainLoop是每一帧调用的函数。我们发现cocos在每一帧结束绘制drawScene之后都会调用PoolManager::getInstance()->getCurrentPool()->clear();的操作。接下去看看clean的实现细节

void AutoreleasePool::clear()
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
    _isClearing = true;
#endif
    std::vector<Ref*> releasings;
    releasings.swap(_managedObjectArray);
    for (const auto &obj : releasings)
    {
        obj->release();
    }
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
    _isClearing = false;
#endif
}

我们发现,在clean里面对所有在_managedObjectArray中的对象都进行了一次release操作,并把它从_managedObjectArray中删除掉。_managedObjectArray是什么,查看前一段代码中addObject的实现细节就知道,autorelease就是把当前对象加入到_managedObjectArray中。

也就是说,我们创建的Label的时候引用计数为1,然后调用autorelease添加到_managedObjectArray中,之后又被addChild到屏幕中,此时引用计数为2。当一帧绘制结束的时候会系统会调用释放池的clear函数,此函数会遍历所有在自动释放池内的对象并release,最后从对象池中删除之(所以第二帧结束后不会被再次调用release了),此时引用计数为1。当我们把当前场景移除的时候会调用release把引用计数减少至0,并从内存中释放。

以上就是cocos的的自动内存管理机制

文/voiddog(简书作者)
原文链接:http://www.jianshu.com/p/98a87e8ed7f3
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

时间: 2024-11-18 17:08:02

cocos内存管理之PoolManager::getInstance()->getCurrentPool()->clear();的相关文章

菜鸟学习Cocos2d-x 3.x——内存管理

菜鸟学习Cocos2d-x 3.x——内存管理 2014-12-10 分类:Cocos2d-x / 游戏开发 阅读(394) 评论(6) 亘古不变的东西 到现在,内存已经非常便宜,但是也不是可以无限大的让你去使用,特别是在移动端,那么点内存,那么多 APP要抢着用,搞不好,你占的内存太多了,系统直接干掉你的APP,所以说了,我们又要老生常谈了——内存管理.总结COM开发的时候,分析过COM的 内存管理模式:总结Lua的时候,也分析了Lua的内存回收机制:前几天,还专门写了C++中的智能指针在内存

cocos2d-x 源码分析 : Ref (CCObject) 源码分析 cocos2d-x内存管理策略

源码版本来自3.x,转载请注明 cocos2d-x 源码分析总目录: http://blog.csdn.net/u011225840/article/details/31743129 1.Ref,AutoreleasePool,PoolManager Ref中包含了一个叫referenceCount的引用计数,当一个Ref类的变量被new的时候,其referenceCount的引用计数被置为1. 其中有三个重要的操作,retain,release,autorelease,下面源码分析时会详细说明

【cocos2d-x 3.x 学习笔记】对象内存管理

内存管理 内存管理一直是一个不易处理的问题,开发者必须考虑分配回收的方式和时机,针对堆和栈做不同的优化处理,等等.内存管理的核心是动态分配的对象必须保证在使用完毕后有效地释放内存,即管理对象的生命周期.由于C++是一个较为底层的语言,其设计上不包含任何智能管理内存的机制.一个对象在使用完毕后必须被回收,然而在复杂的程序中,对象所有权在不同程序片段间传递或共享,使得确定回收的时机十分困难,因此内存管理成为了程序员十分头疼的问题. 另一方面,过于零散的对象分配回收可能导致堆中的内存碎片化,降低内存的

cocos2d-x 源代码分析 : Ref (CCObject) 源代码分析 cocos2d-x内存管理策略

从源代码版本号3.x.转载请注明 cocos2d-x 总的文件夹的源代码分析: http://blog.csdn.net/u011225840/article/details/31743129 1.Ref,AutoreleasePool.PoolManager Ref中包括了一个叫referenceCount的引用计数,当一个Ref类的变量被new的时候,其referenceCount的引用计数被置为1. 当中有三个重要的操作,retain.release,autorelease,以下源代码分析

Cocos2d-x学习笔记—内存管理机制

Cocos2d-x 3.x内存管理机制 1:C++内存管理 1-1:内存分配区域 创建对象需要两个步骤:第一步,为对象分配内存:第二步,调用构造函数初始化内存.在第一步中,可以选择几个不同的分配区域.这几个区域如下: (1) 栈区域分配.栈内存分配运算内置于处理器的指令集中,效率很髙,但是分配的内 存容量有限.由处理器自动分配和释放,用来存放函数的参数值和局部变量的值等.在执 行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放. (2) 堆区域分配.从堆上分

2、COCOS2D-X内存管理机制

在C++中,动态内存分配是一把双刃剑,一方面,直接访问内存地址提高了应用程序的性能,与使用内存的灵活性:另一方面,由于程序没有正确地分配与释放造成的例如野指针,重复释放,内存泄漏等问题又严重影响着应用程序的稳定性. 人们尝试着不同的方案去避免这个问题,比较常用的如智能指针,自动垃圾回收等,这些要么影响了应用程序的性能,要么仍然需要依赖于开发者注意一些规则,要么给开发者带来了另外一些很丑陋的用法(实际上笔者很不喜欢智能指针).因此,优秀的C++内存管理方案需要兼顾性能,易用性,所以到目前为止C++

浅谈自己对cocos2dx的内存管理的理解

拿个自己写Layer的例子 1 auto genMenuWnd = GeneralMenuWnd::create(); 2 CC_BREAK_IF(!genMenuWnd); 3 addChild(genMenuWnd, 100); 该引用计数变化流程 GeneralMenuWnd在create()的时候,其_referenceCount 为1,并在create()的时候autorelease(); addChild()的时候,其_referenceCount变为2: 当该帧执行完后,_refe

8、Cocos2dx 3.0游戏开发找小三之3.0版本的内存管理

重开发者的劳动成果,转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/27693365 复杂的内存管理 移动设备上的硬件资源十分有限,内存尤为宝贵,开发者必须十分慎重地利用内存,避免不必要的消耗,更要防止内存泄漏. 基于 Cocos2d-iPhone 的 Objective-C风格的内存管理是 Cocos2d-x 的一个特色. 把 Objective-C 的内存管理方式引入 C++,使得游戏开发的内存管理难度下降了个层次.

Cocos2d之&ldquo;引用计数&rdquo;内存管理机制实现解析

一.引言 本文主要分析cocos2d游戏开发引擎的引用计数内存管理技术的实现原理.建议读者在阅读本文之前阅读笔者之前一篇介绍如何使用cocos2d内存管理技术的文章--<Cocos2d之Ref类与内存管理使用详解>. 二.相关概念 引用计数 引用计数是计算机编程语言的一种内存管理技术,是指将资源(对象.内存或者磁盘空间等)的被引用计数保存起来,当引用计数变为零时就将资源释放的过程.使用引用计数技术可以实现自动内存管理的目的. 当实例化一个类时,对象的引用计数为1,在其他对象需要持有这个对象时,