cocos2d-x 内存管理浅析
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__, 上述介绍了一下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 内存管理浅析的更多相关文章
- 内存管理 & 内存优化技巧 浅析
内存管理 浅析 下列行为都会增加一个app的内存占用: 1.创建一个OC对象: 2.定义一个变量: 3.调用一个函数或者方法. 如果app占用内存过大,系统可能会强制关闭app,造成闪退现象,影响用户 ...
- Linux内存管理原理
本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址又叫线性地址.linux没有采用分段机制,所以逻辑地址和虚拟地址(线性地址)(在用户态,内核态逻 ...
- Linux内存管理原理【转】
转自:http://www.cnblogs.com/zhaoyl/p/3695517.html 本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址 ...
- Windows内存管理和linux内存管理
windows内存管理 windows 内存管理方式主要分为:页式管理,段式管理,段页式管理. 页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页:页式管理把内存空间按照页的大小划分成片或 ...
- 转 Linux内存管理原理
Linux内存管理原理 在用户态,内核态逻辑地址专指下文说的线性偏移前的地址Linux内核虚拟3.伙伴算法和slab分配器 16个页面RAM因为最大连续内存大小为16个页面 页面最多16个页面,所以1 ...
- 【转帖】linux内存管理原理深入理解段式页式
linux内存管理原理深入理解段式页式 https://blog.csdn.net/h674174380/article/details/75453750 其实一直没弄明白 linux 到底是 段页式 ...
- 浅析java内存管理机制
内存管理是计算机编程中的一个重要问题,一般来说,内存管理主要包括内存分配和内存回收两个部分.不同的编程语言有不同的内存管理机制,本文在对比C++和Java语言内存管理机制的不同的基础上,浅析java中 ...
- C#编程(七十三)----------浅析C#中内存管理
浅析C#中内存管理 前言:个人觉得C#吸收了各种语言的优点,可谓集大成者,但是不知但,这种集所有语言于一身的情况是好是坏.C#编程的一个优点就是程序员不需要关心具体的内存管理,尤其是垃圾收集器会处理所 ...
- cocos2d内存管理
设想如下场景, 这是一个典型的内存合理分配的场景: 在一帧内, 有若干个函数, 每个函数都会创建一系列的精灵, 每个精灵都不同, 都会占用一定的内存, 精灵的总数可能会有1000个, 而一个函数只会创 ...
随机推荐
- 模拟退火解决TSP问题
// monituihuo.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <stdio.h> #includ ...
- re正则表达式7_{}
curly brackets {} instead of one number, you can specify a range by writing a minimum,a comma,and a ...
- Java代码块
代码块分为普通代码块.构造块.静态代码块.同步代码块4种 普通代码块 普通代码块是指直接在方法或者是语句中定义的代码块 构造块 构造块是直接写在类中的代码块 构造块优先于构造方法执行,而且每次实例化对 ...
- HTML 通知公告练习
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 新浪微博客户端(50)-解决输入Emotion表情逐渐变小的问题
UITextView+Extension.h #import <UIKit/UIKit.h> @interface UITextView (Extension) /** 插入属性文本 */ ...
- java设计模式设计模式
JAVA设计模式之单例模式 概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一 ...
- js jquery, jquery-ui 获取form各种表单input的值?
如何获取? make up (for): 弥补, 补偿, her beaty cannot make up for her stu'pidity. five Basic laws of human s ...
- 如何判断PHP 是线程安全还是非线程安全的
什么是线程安全与非线程安全? 线程安全就是在多线程环境下也不会出现数据不一致,而非线程安全就有可能出现数据不一致的情况. 线程安全由于要确保数据的一致性,所以对资源的读写进行了控制,换句话说增加了系统 ...
- CF467 AB 水题
Codeforces Round #267 (Div. 2) (C和D的题解单独写:CF467C George and Job (DP) CF467D Fedor and Essay 建图DFS) C ...
- hdu4888 Redraw Beautiful Drawings 最大流+判环
hdu4888 Redraw Beautiful Drawings Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 65536/6553 ...