Cocos2d-x 3.2 的内存管理详解
本文首先介绍 Cocos2d-x 3.2 中内存管理的作用,以及各个作用的应用。借由通俗易懂的解释来了解内存管理的过程。其次通过源码解析介绍其内部的实现原理。加深理解,从而在有需要的时候绕开引擎建立自己的内存管理机制。 一、Cocos2d-x 3.2 内存管理的两个方面 ? 1、及时释放弃用的对象 使用条件:该对象是Node的子类对象 内存管理过程: addChild //添加对象后,对象可以被使用
removeChild //删除对象后,对象被立刻删除(通过 delete)
2、及时释放未使用的对象 简述:新创建的对象如果一帧内不使用,就会被自动释放。(所谓一帧,即是一个gameloop。) 内存管理过程:
引用的初始值为0,如果一帧结束后对象的引用值还是0,那就就会被 delete。
^^ 版权衔接线 vv ???
全文出处:http://www.cnblogs.com/tangyikejun/p/4361638.html
二、内存管理的实现原理涉及内存管理的文件很多,仅展示直接相关的部分代码。 1、第一部分 AutoreleasePool类:管理一个 PoolManager类:管理一个 DisplayLinkDirector类:这是一个导演类,提供游戏的主循环,实现每一帧的资源释放。这个类的名字看起来有点怪,但是不用管它。因为这个类继承了 1.1 Ref// 引用计数变量
unsigned int _referenceCount;
// 对象被构造后,引用计数值为 1
Ref::Ref()
: _referenceCount(1) //当Ref对象被创建时,引用计数的值为 1
{
#if CC_ENABLE_SCRIPT_BINDING
static 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,21)">"reference count should greater than 0");
--_referenceCount;
if (_referenceCount == 0)
{
if CC_USE_MEM_LEAK_DETECTION
untrackRef(endif
delete this; // 注意这里 把对象 delete 了
}
}
// 提供加入自动释放池的接口。对象调用此函数即可加入自动释放池的管理。
Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
//获取引用计数值
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) auto& obj : _managedObjectArray)
{
if (obj == object)
true;
}
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() return _releasePoolStack.back();
}
// 查看对象是否在某个释放池内
bool PoolManager::isObjectInPools(Ref* obj) auto& pool : _releasePoolStack)
{
if (pool->contains(obj))
false;
}
// 添加释放池对象
void PoolManager::push(AutoreleasePool *pool)
{
_releasePoolStack.push_back(pool);
}
// 释放池对象出栈
void PoolManager::pop()
{
CC_ASSERT(!_releasePoolStack.empty());
_releasePoolStack.pop_back();
}
1.4 DisplayLinkDirectorvoid DisplayLinkDirector::mainLoop()
{
//第一次当导演
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();//进行清理工作
}
else if (! _invalid)
{
// 绘制场景,游戏主要工作都在这里完成
drawScene();
// 清空资源池
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
根据目前的分析,我们先来捋一捋,待会儿再进一步深入。内存管理的过程是怎么样的呢?首先,创建了一个 接下来我们继续介绍另外几个与内存管理有关的类。 2、第二部分 2.1 Node// 添加节点
void Node::addChild(Node *child)
{
CCASSERT( child != nullptr,21)">"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,21)">"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(),21)">"Invalid index!");
auto it = std::next( begin(),index );
(*it)->release(); // 进行了release
return _data.erase(it);
}
到这里,终于可以把故事完整地讲一遍了。内存管理的过程是怎么样的呢?首先,创建了一个 3、高阶用法之所以称为高阶用法,是因为,如果开发者对 Cocos 的内存管理机制理解不够深刻,那么很可能会用错而导致损失大于收益。另一方面,这类用法在平时很少会用到。 3.1 使用
|