Cocos2d-x用create创建对象,

这个方法已经被引擎封装成一个宏定义了:CREATE_FUNC,

下面是这个宏定义的实现:

#define CREATE_FUNC(__TYPE__) \  
static __TYPE__* create() \  
{ \  
    __TYPE__ *pRet = new __TYPE__(); \  
    if (pRet && pRet->init()) \  
    { \  
        pRet->autorelease(); \  
        return pRet; \  
    } \  
    else \  
    { \  
        delete pRet; \  
        pRet = NULL; \  
        return NULL; \  
    } \  
}
 
 
可以看到它在其中首先new了这个类__TYPE__,
这时候new出来的对象的引用计数为1,
然后初始化完成后,
这里执行了autorelease,
这时候引用计数仍然为1,
但是引擎将其加入了自动释放池,
在这一帧结束的时候,
这个对象的引用计数将变为0,
引用计数为0的对象将会被释放掉。

上述介绍了一下Cocos2d-x的内存管理机制,

现在进入正文了,

当一个节点被加入到UI树中,

它的引用计数将会有怎么样的变化呢?

下面是Node的addChild的源码分析

(addChild中真正的实现在addChildHelper中,下文忽略了不相关的代码):

void Node::addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag)  
{  
    this->insertChild(child, localZOrder);  
       
    if (setTag)  
        child->setTag(tag);  
    else  
        child->setName(name);  
       
    child->setParent(this);  
    child->setOrderOfArrival(s_globalOrderOfArrival++);  
}

可以看到真正的实现是在insertChild这个函数中的,

我们继续尾随进去:

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

这里将child加入到了_children中,

_children是什么呢?

看它的声明

Vector<Node*> _children;

注意,

这是一个大写V开头的Vector,

说明这是Cocos2d-x自己实现的可变数组,

这个数组实际上和std标准库中的数组的实现差不多,

标准库的算法可以完美的应用在这个数组上,

这个数组与std::vector的最大区别就是引入了引用技术机制。

在pushBack中,究竟做了些什么呢?

void pushBack(T object)  
{  
    CCASSERT(object != nullptr, "The object should not be nullptr");  
    _data.push_back( object );  
    object->retain();  
}

没错,重点在于这里

object->retain();

它对于添加进来的对象都增加了引用,

这样就说明,

所有被加入UI树中的节点都会被UI树保持强引用。

接下来对于removeXXXX函数进行分析,

就挑选removeChild函数进行分析吧

void Node::removeChild(Node* child, bool cleanup /* = true */)  
{  
    // explicit nil handling  
    if (_children.empty())  
    {  
        return;  
    }  
   
    ssize_t index = _children.getIndex(child);  
    if( index != CC_INVALID_INDEX )  
        this->detachChild( child, index, cleanup );  
}

而这个函数最终调用的是 detachChild函数,

来继续跟踪进去吧(忽略的无关代码)

void Node::detachChild(Node *child, ssize_t childIndex, bool doCleanup)  
{  
    // set parent nil at the end  
    child->setParent(nullptr);  
   
    _children.erase(childIndex);  
}

这里的重点代码就是

_children.erase(childIndex);

同样跟踪进入看看它的实现:

iterator erase(ssize_t index)  
{  
    CCASSERT(!_data.empty() && index >=0 && index < size(), "Invalid index!");  
    auto it = std::next( begin(), index );  
    (*it)->release();  
    return _data.erase(it);  
}

没错,它执行了下面这句代码:

(*it)->release();

减少了对象的引用计数,

这样就能将UI从UI树中分离并且不会造成内存泄露了。

当然,

这样做的好处还不止这些,

试想如下代码

Scene* s = Scene::create();  
Director::getInstance()->runWithScene(s);  
Layer* l = Layer::create();  
s->addChild(l);  
   
.... 若干帧后  
   
s->removeChild(l);

是否会造成内存泄露?

答案是不会,

而且这样写出来的代码,

我们并不需要关心内存的分配问题,

引擎会自动帮我们申请内存,

并且在不需要的时候,

自动将内存回收。

这似乎是一个非常好的解决方案,

但是也有一些不足。

试想如下使用场景,

现需要将上述的l节点与s节点中间增加一个层m,

m是s场景的子节点,

也是l层的父节点,

这时候应该怎么做呢?

要知道在removeChild之后,

l层的内存已经被释放掉了。

似乎没有什么解决方法了,

看下文:

Scene* s = Scene::create();  
Director::getInstance()->runWithScene(s);  
Layer* l = Layer::create();  
l->setTag(1);  
s->addChild(l);  
   
.... 若干帧后  
   
auto l = s->getChildByTag(1);  
l->retain();  
s->removeChild(l);  
Layer* m = Layer::create();  
s->addChild(m);  
m->addChild(l);  
l->release();

这样提前将l取出来增加一个引用计数就可以避免l的内存被UI树释放掉了,

但是值得注意的是,

retain方法必须与release方法对应出现,

否则会造成内存泄露。

但是开发者往往会忘记写后面的release从而造成内存泄露,

那么怎么避免这样的情况出现呢,

答案是:智能指针。

看下面的代码

Scene* s = Scene::create();  
Director::getInstance()->runWithScene(s);  
Layer l = Layer::create();  
l->setTag(1);  
s->addChild(l);  
   
.... 若干帧后  
   
RefPtr<Node*> l = s->getChildByTag(1);  
s->removeChild(l);  
Layer m = Layer::create();  
s->addChild(m);  
m->addChild(l);
 

非常简单方便,

完全不需要关心内存的申请和释放,

关于智能指针部分以后会对其做出分析。

cocos2d-x 内存管理浅析的更多相关文章

  1. 内存管理 & 内存优化技巧 浅析

    内存管理 浅析 下列行为都会增加一个app的内存占用: 1.创建一个OC对象: 2.定义一个变量: 3.调用一个函数或者方法. 如果app占用内存过大,系统可能会强制关闭app,造成闪退现象,影响用户 ...

  2. Linux内存管理原理

    本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址又叫线性地址.linux没有采用分段机制,所以逻辑地址和虚拟地址(线性地址)(在用户态,内核态逻 ...

  3. Linux内存管理原理【转】

    转自:http://www.cnblogs.com/zhaoyl/p/3695517.html 本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址 ...

  4. Windows内存管理和linux内存管理

    windows内存管理 windows 内存管理方式主要分为:页式管理,段式管理,段页式管理. 页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页:页式管理把内存空间按照页的大小划分成片或 ...

  5. 转 Linux内存管理原理

    Linux内存管理原理 在用户态,内核态逻辑地址专指下文说的线性偏移前的地址Linux内核虚拟3.伙伴算法和slab分配器 16个页面RAM因为最大连续内存大小为16个页面 页面最多16个页面,所以1 ...

  6. 【转帖】linux内存管理原理深入理解段式页式

    linux内存管理原理深入理解段式页式 https://blog.csdn.net/h674174380/article/details/75453750 其实一直没弄明白 linux 到底是 段页式 ...

  7. 浅析java内存管理机制

    内存管理是计算机编程中的一个重要问题,一般来说,内存管理主要包括内存分配和内存回收两个部分.不同的编程语言有不同的内存管理机制,本文在对比C++和Java语言内存管理机制的不同的基础上,浅析java中 ...

  8. C#编程(七十三)----------浅析C#中内存管理

    浅析C#中内存管理 前言:个人觉得C#吸收了各种语言的优点,可谓集大成者,但是不知但,这种集所有语言于一身的情况是好是坏.C#编程的一个优点就是程序员不需要关心具体的内存管理,尤其是垃圾收集器会处理所 ...

  9. cocos2d内存管理

    设想如下场景, 这是一个典型的内存合理分配的场景: 在一帧内, 有若干个函数, 每个函数都会创建一系列的精灵, 每个精灵都不同, 都会占用一定的内存, 精灵的总数可能会有1000个, 而一个函数只会创 ...

随机推荐

  1. iOS开发-二维码

    二维码 从ios7开始集成了二维码的生成和读取功能 此前被广泛使用的zbarsdk目前不支持64位处理器 生成二维码的步骤: 倒入CoreImage框架 通过滤镜CIFilter生成二维码 二维码的内 ...

  2. c中三大区的解析

    1.栈,堆和静态存储区是 堆和静态存储区是C语言程序常涉及的三个基本内语言程序常涉及的三个基本内存区2.栈区主要用于函数调用的使用3.堆区主要是用于内存的动态申请和归还4.静态存储区用于保存全局变量和 ...

  3. centos Apache、php、mysql默认安装路径

    apache: 如果采用RPM包安装,安装路径应在 /etc/httpd目录下 apache配置文件:/etc/httpd/conf/httpd.conf Apache模块路径:/usr/sbin/a ...

  4. 9月5日网页基础知识 通用标签、属性(body属性、路径、格式控制) 通用标签(有序列表、无序列表、常用标签)(补)

    网页基础知识 一.HTML语言 HTML语言翻译汉语为超文本标记语言. 二.网页的分类 1.静态页面:在静态页面中修改网页内容实际上就是修改网页原代码,不能从后台操作,数据来只能来源于原于代码.静态网 ...

  5. JavaWeb学习笔记——Tomcat数据源

    server.xml配置数据帐号和密码等

  6. CSS学习笔记——响应式布局

    响应式布局 响应式布局是现在很流行的一个设计理念,随着移动互联网的盛行,为解决如今各式各样的浏览器分辨率以及不同移动设备的显示效果,设计师提出了响应式布局的设计方案.所谓的响应式布局,就是一个网站能够 ...

  7. PHP----Ajax异步请求

    需要两个PHP页面:1.php是发出请求和接受请求结果的.2.php是处理请求的结果. 1.php中代码: <a href="#" onclick="sendAja ...

  8. Dynamic Font Programming

    http://www.braynzarsoft.net/Articles/index.php?p=VA&article=Easy-Font-Rendering-in-DirectX-11 ht ...

  9. struts+service+action+数据库

    用户登录流程 1.jsp根据form表单中的action的login   <form action="/test02/login" method="post&quo ...

  10. PHP数组处理函数的使用array_push(一)

    使用PHP做数据处理时会涉及到大量的数组操作,这里我就记下各种数组处理函数的使用方法,好记性不如烂笔头!! 一.array_push的使用方法:将一个或多个单元压入数组的末尾(入栈) 说明:int a ...