Cocos2d-x 3.2 的内存管理详解
2015-03-24 14:33
302 查看
本文首先介绍 Cocos2d-x 3.2 中内存管理的作用,以及各个作用的应用。借由通俗易懂的解释来了解内存管理的过程。其次通过源码解析介绍其内部的实现原理。加深理解,从而在有需要的时候绕开引擎建立自己的内存管理机制。
1) 通过加入
2) 通过节点管理来保证对象在弃用后及时地删除。
使用条件:该对象是Node的子类对象
使用方法:addChild、removeChild
内存管理过程:
简述:新创建的对象如果一帧内不使用,就会被自动释放。(所谓一帧,即是一个gameloop。)
使用条件:对象通过
使用方法:自动实现
内存管理过程:
对象不使用的情况
对象使用的情况
引用的初始值为0,如果一帧结束后对象的引用值还是0,那就就会被 delete。
^^ 版权衔接线 vv 全文出处:/content/5045501.html
涉及内存管理的文件很多,仅展示直接相关的部分代码。
根据目前的分析,我们先来捋一捋,待会儿再进一步深入。内存管理的过程是怎么样的呢?首先,创建了一个
接下来我们继续介绍另外几个与内存管理有关的类。
到这里,终于可以把故事完整地讲一遍了。内存管理的过程是怎么样的呢?首先,创建了一个
之所以称为高阶用法,是因为,如果开发者对 Cocos 的内存管理机制理解不够深刻,那么很可能会用错而导致损失大于收益。另一方面,这类用法在平时很少会用到。
在开发过程中,如果需要使用一个节点对象,但是又不想把它放到节点树里面去。那么就可以使用
有些情况下,希望闲置对象晚一帧进行销毁,可以使用
笔者本身还没有机会使用过高阶用法,如果有小伙伴发现了高阶用法在实际问题中的应用,敬请留言交流。
源地址:/content/5045501.html?utm_source=tuicool
一、Cocos2d-x 3.2 内存管理的两个方面
1) 通过加入
autorelease来自动释放那些创建后未使用的对象。
2) 通过节点管理来保证对象在弃用后及时地删除。
1、及时释放弃用的对象
使用条件:该对象是Node的子类对象使用方法:addChild、removeChild
内存管理过程:
addChild //添加对象后,对象可以被使用 removeChild //删除对象后,对象被立刻删除(通过 delete)
2、及时释放未使用的对象
简述:新创建的对象如果一帧内不使用,就会被自动释放。(所谓一帧,即是一个gameloop。)使用条件:对象通过
CREAT_FUNC()宏创建或者对象使用
autorelease加入了自动释放池。
使用方法:自动实现
内存管理过程:
对象不使用的情况
对象创建 引用+1 对象自动释放 引用-1
对象使用的情况
对象创建 引用+1 对象使用 引用+1 // 通过 addChild 使用对象 对象自动释放 引用-1
引用的初始值为0,如果一帧结束后对象的引用值还是0,那就就会被 delete。
^^ 版权衔接线 vv 全文出处:/content/5045501.html
二、内存管理的实现原理
涉及内存管理的文件很多,仅展示直接相关的部分代码。
1、第一部分
Ref类: 进行引用计数、提供加入自动释放池的接口。
AutoreleasePool类: 管理一个
vector数组来存放加入自动释放池的对象。提供对释放池的清空操作。
PoolManager类: 管理一个
vector数组来存放自动释放池。默认情况下引擎只创建一个自动释放池,因此这个类是提供给开发者使用的,例如出于性能考虑添加自己的自动释放池。
DisplayLinkDirector类: 这是一个导演类,提供游戏的主循环,实现每一帧的资源释放。这个类的名字看起来有点怪,但是不用管它。因为这个类继承了
Director类,也是唯一一个继承了
Director的类,也就是说完全可以合并为一个类,引擎开发者在源码中有部分说明。
1.1 Ref
// 引用计数变量 unsigned int _referenceCount; // 对象被构造后,引用计数值为 1 Ref::Ref() : _referenceCount(1) //当Ref对象被创建时,引用计数的值为 1 { #if CC_ENABLE_SCRIPT_BINDING static unsigned int uObjectCount = 0; _luaID = 0; _ID = ++uObjectCount; _scriptObject = nullptr; #endif #if CC_USE_MEM_LEAK_DETECTION trackRef(this); #endif } // 引用+1 void Ref::retain() { CCASSERT(_referenceCount > 0, "reference count should greater than 0"); ++_referenceCount; } // 引用-1 。如果引用为0则释放对象 void Ref::release() { CCASSERT(_referenceCount > 0, "reference count should greater than 0"); --_referenceCount; if (_referenceCount == 0) { #if CC_USE_MEM_LEAK_DETECTION untrackRef(this); #endif delete this; // 注意这里 把对象 delete 了 } } // 提供加入自动释放池的接口。对象调用此函数即可加入自动释放池的管理。 Ref* Ref::autorelease() { PoolManager::getInstance()->getCurrentPool()->addObject(this); return this; } //获取引用计数值 unsigned int Ref::getReferenceCount() const { return _referenceCount; }
1.2 AutoreleasePool
// 存放释放池对象的数组 std::vector<Ref*> _managedObjectArray; // 往释放池添加对象 void AutoreleasePool::addObject(Ref* object) { _managedObjectArray.push_back(object); } // 清空释放池,将其中的所有对象都 delete void AutoreleasePool::clear() { // 释放所有对象 for (const auto &obj : _managedObjectArray) { obj->release(); } // 清空vector数组 _managedObjectArray.clear(); } // 查看某个对象是否在释放池中 bool AutoreleasePool::contains(Ref* object) const { for (const auto& obj : _managedObjectArray) { if (obj == object) return true; } return false; }
1.3 PoolManager
// 释放池管理器单例对象 static PoolManager* s_singleInstance; // 释放池数组 std::vector<AutoreleasePool*> _releasePoolStack; // 获取 释放池管理器的单例 PoolManager* PoolManager::getInstance() { if (s_singleInstance == nullptr) { // 新建一个管理器对象 s_singleInstance = new PoolManager(); // 添加一个自动释放池 new AutoreleasePool("cocos2d autorelease pool");// 内部使用了释放池管理器的push,这里的调用很微妙,读者可以动手看一看 } return s_singleInstance; } // 获取当前的释放池 AutoreleasePool* PoolManager::getCurrentPool() const { return _releasePoolStack.back(); } // 查看对象是否在某个释放池内 bool PoolManager::isObjectInPools(Ref* obj) const { for (const auto& pool : _releasePoolStack) { if (pool->contains(obj)) return true; } return false; } // 添加释放池对象 void PoolManager::push(AutoreleasePool *pool) { _releasePoolStack.push_back(pool); } // 释放池对象出栈 void PoolManager::pop() { CC_ASSERT(!_releasePoolStack.empty()); _releasePoolStack.pop_back(); }
1.4 DisplayLinkDirector
void DisplayLinkDirector::mainLoop() { //第一次当导演 if (_purgeDirectorInNextLoop) { _purgeDirectorInNextLoop = false; purgeDirector();//进行清理工作 } else if (! _invalid) { // 绘制场景,游戏主要工作都在这里完成 drawScene(); // 清空资源池 PoolManager::getInstance()->getCurrentPool()->clear(); } }
根据目前的分析,我们先来捋一捋,待会儿再进一步深入。内存管理的过程是怎么样的呢?首先,创建了一个
Node对象A,
Node继承Ref,因此
Ref的引用计数为1;然后,A通过
autorelease将自己放入自动释放池;
drawScene()完成后,一帧结束,
Director通过释放池将池中的对象
clear(),即对
Node对象A进行
release()操作。A的引用计数变为0,执行
delete释放A对象。
接下来我们继续介绍另外几个与内存管理有关的类。
2、第二部分
Node类:提供了
addChild和
removeChild方法来创建游戏的节点树。
Vector类:封装了对于对象的
retain操作和
release操作。
2.1 Node
// 添加节点 void Node::addChild(Node *child) { CCASSERT( child != nullptr, "Argument must be non-nil"); this->addChild(child, child->_localZOrder, child->_name); // 经过这个方法-->addChildHelper-->insertChild,完成retain操作 } // 移除节点 void Node::removeChild(Node* child, bool cleanup /* = true */) { // if (_children.empty()) { return; } // ssize_t index = _children.getIndex(child); if( index != CC_INVALID_INDEX ) this->detachChild( child, index, cleanup );//注意这个函数 } // 插入节点 void Node::insertChild(Node* child, int z) { _transformUpdated = true; _reorderChildDirty = true; _children.pushBack(child);// pushBack方法对节点进行了retain child->_setLocalZOrder(z); } // 剥离节点 void Node::detachChild(Node *child, ssize_t childIndex, bool doCleanup) { ...// 部分省略 _children.erase(childIndex);// erase方法对节点进行了release }
2.2 Vector
// 这里仅展示与Node类相关的内存管理的部分 // 将对象入栈,引用+1 void pushBack(T object) { CCASSERT(object != nullptr, "The object should not be nullptr"); _data.push_back( object ); object->retain(); // 进行了retain } // 将目标位置的对象移除 iterator erase(ssize_t index) { CCASSERT(!_data.empty() && index >=0 && index < size(), "Invalid index!"); auto it = std::next( begin(), index ); (*it)->release(); // 进行了release return _data.erase(it); }
到这里,终于可以把故事完整地讲一遍了。内存管理的过程是怎么样的呢?首先,创建了一个
Node对象A,
Node继承
Ref,因此
Ref的引用计数为1;然后A又通过
autorelease将自己放入自动释放池;接着,有个
Node对象B,B通过
addChild(A)使得A的引用+1;几个
mainLoop后,B通过
removeChild(A)使得A的引用-1;这个
mainLoop的
drawScene()完成后,一帧结束,
Director通过释放池将池中的对象
clear(),即对
Node对象A进行
release操作。A的引用计数变为0,执行
delete释放A对象。
3、高阶用法
之所以称为高阶用法,是因为,如果开发者对 Cocos 的内存管理机制理解不够深刻,那么很可能会用错而导致损失大于收益。另一方面,这类用法在平时很少会用到。
3.1 使用 retain
来延长对象的生存时间
在开发过程中,如果需要使用一个节点对象,但是又不想把它放到节点树里面去。那么就可以使用 retain来避免对象被自动释放掉。
3.2 使用 PoolManager
的 push
来延长对象的生存时间
有些情况下,希望闲置对象晚一帧进行销毁,可以使用 push把当前释放池推入栈底,那么这一帧结束的时候只会释放刚
push进去的释放池。
笔者本身还没有机会使用过高阶用法,如果有小伙伴发现了高阶用法在实际问题中的应用,敬请留言交流。
源地址:/content/5045501.html?utm_source=tuicool
相关文章推荐
- Cocos2d-x 3.2 的内存管理详解
- 《Cocos2d-x-3.2 Lua-tests》文件详解 之 缓动ActionsEase
- 《Cocos2d-x-3.2 Lua-tests》文件详解 之 动作Actions
- 《Cocos2d-x-3.2 Lua-tests》文件详解 之 加速器
- 《Cocos2d-x-3.2 Lua-tests》文件详解 之 截屏CaptreScreen
- Mac下搭建Cocos2d-x-3.2的开发环境详解
- cocos2d-x内存管理机制详解
- 《Cocos2d-x-3.2 Lua-tests》文件详解 之 进度条ActionsProgress
- 《Cocos2d-x-3.2 Lua-tests》文件详解 之 特效Effects
- 《Cocos2d-x-3.2 Lua-tests》文件详解 之 动作管理ActionManager
- 《Cocos2d-x-3.2 Lua-tests》文件详解 之 基本绘图DrawPrimitives
- 《Cocos2d-x-3.2 Lua-tests》文件详解 之 视差Parallax
- 踏入C++中的雷区——C++内存管理详解(I)
- C++内存管理详解
- 踏入C++中的雷区——C++内存管理详解(转)
- 踏入C++中的雷区——C++内存管理详解(III)
- 【linux编程】C++内存管理详解(一)
- 经典收藏 C++内存管理操作详解
- 踏入C++中的雷区——C++内存管理详解(IV)
- C++内存管理详解(收藏)