注: 本文从个人博客园摘录而来。
Ref
cocos2d-x中几乎所有的对象节点继承于Ref ,而Ref 主要对对象进行引用技术管理 。
class CC_DLL Ref:
{
public:
void retain(); // 增加引用计数
void release(); // 减少引用计数,引用计数为0时进行释放
Ref* autorelease(); // 添加自动缓存池
unsigned int getReferenceCount(); // 获取对象引用计数
protected:
Ref(); // 构造函数设为protected,保证可被继承且智能子类实例化
public:
virtual ~Ref();
protected:
unsigned int _referenceCount; // 引用计数数目
friend class AutoreleasePool; // 自动释放池
}
其主要接口为:
// 引用计数+1
void Ref::retain()
{
CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
++_referenceCount;
}
// 引用计数-1
void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
--_referenceCount;
// 引用计数为0时,销毁对象
if (_referenceCount == 0)
{
delete this;
}
}
简单理解,就是retain 和relese 对对象节点进行了引用计数的加减1操作而已。
如果引用计数为0的时候,对象就会被销毁掉。
通过new 创建一个对象,简单的示例:
auto node = new Node(); // 引用计数为1
addChild(node); // 引用计数为2
node->removeFromParent(); // 引用计数为1
node->release(); // 引用计数为0,销毁对象
autoRelease
retain() 与release() 接口要配对使用。
上面的例子,如果忘记调用release() 接口,就会导致内存泄漏。
为此Ref 提供了autoRelease() 来帮助我们自动管理内存的释放相关。
// 添加对象到自动释放池中
Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
// 对象会被添加到_managedObjectArray的vector容器中
// std::vector<Ref*> _managedObjectArray;
void AutoreleasePool::addObject(Ref* object)
{
_managedObjectArray.push_back(object);
}
而对于autorelease 的使用,主要体现在cocos2d-x提供的静态方法:create 中
此方法又称为二次构建模式 , 主要是为了简化程序对节点的创建,以及忘记初始化相关。
// CCNode.cpp
Node * Node::create()
{
Node * ret = new (std::nothrow) Node();
if (ret && ret->init())
{
ret->autorelease(); // 将对象添加到自动释放池中
}
else
{
CC_SAFE_DELETE(ret);
}
return ret;
}
// CCSprite.cpp
Sprite* Sprite::create(const std::string& filename)
{
Sprite *sprite = new (std::nothrow) Sprite();
if (sprite && sprite->initWithFile(filename))
{
sprite->autorelease(); // 将对象添加到自动释放池中
return sprite;
}
CC_SAFE_DELETE(sprite);
return nullptr;
}
// UIButton.cpp
Button* Button::create()
{
Button* widget = new (std::nothrow) Button();
if (widget && widget->init())
{
widget->autorelease(); // 将对象添加到自动释放池中
return widget;
}
CC_SAFE_DELETE(widget);
return nullptr;
}
cocos2d-x会在每帧结束时,清理当前自动释放池中的对象。
void Director::mainLoop()
{
if (! _invalid)
{
drawScene();
// 清理当前释放池对象
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
void AutoreleasePool::clear()
{
// 千万注意此处
std::vector<Ref*> releasings;
releasings.swap(_managedObjectArray);
// 遍历所有对象,进行引用计数-1,为0的销毁对象
for (const auto &obj : releasings)
{
obj->release();
}
}
如果一个对象在创建后,在当前帧结束没有被使用(addChild) , 对象就会被销毁掉。
local node = cc.Node:create()
print("引用计数为:", node:getReferenceCount())
local delay = cc.DelayTime:create(1)
local action = cc.Sequence:create(delay, cc.CallFunc:create(function()
print("引用计数为:", node:getReferenceCount())
end))
self._root:runAction(action)
所以对象一定要在当前帧结束前调用(addChild)。
local node = cc.Node:create()
print("引用计数为:", node:getReferenceCount())
self._root:addChild(node)
print("引用计数为:", node:getReferenceCount())
local delay = cc.DelayTime:create(1)
local action = cc.Sequence:create(delay, cc.CallFunc:create(function()
print("引用计数为:", node:getReferenceCount())
end))
self._root:runAction(action)
end
在对象addChild 之后,引用计数由2变为1的缘故,在于:
void AutoreleasePool::clear()
{
// 使用临时对象,交互容器数据
// _managedObjectArray会被置空,releasings在函数运行结束后也会被释放
std::vector<Ref*> releasings;
releasings.swap(_managedObjectArray);
}
当对象不再使用后,通过:
removeFromParent 来删除对象,引用计数由1变为0,对象会被释放- 退出场景,每个节点会被调用
release ,完成场景节点树的释放
// 比如removeFromParent的释放
void Node::removeFromParent()
{
this->removeFromParentAndCleanup(true);
}
void Node::removeFromParentAndCleanup(bool cleanup)
{
if (_parent != nullptr)
{
_parent->removeChild(this,cleanup);
}
}
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 );
}
void Node::detachChild(Node *child, ssize_t childIndex, bool doCleanup)
{
// ..
_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(); // Ref::release()
return _data.erase(it);
}
以此,实现了Ref的自动化管理。
PoolManager
自动释放池是由PoolManager 管理类控制的。
/*
* 构造函数私有化,且创建了getInstance(), 说明该类是个单例模式,全局仅有一个实例对象
* 析构函数私有化,且创建了destroyInstance(), 说明该类放置在栈中创建对象
*/
class CC_DLL PoolManager
{
public:
static PoolManager* getInstance();
static void destroyInstance();
// 获取当前释放池
AutoreleasePool *getCurrentPool() const;
// 判定指定节点是否在对象池中
bool isObjectInPools(Ref* obj) const;
private:
PoolManager();
~PoolManager();
// 压入池数据
void push(AutoreleasePool *pool);
// 移除池数据
void pop();
static PoolManager* s_singleInstance;
std::vector<AutoreleasePool*> _releasePoolStack;
};
该类在初始化状态下会默认添加创建一个AutorelesePool 的池对象。主要原因在于存储cocos2d-x UI节点对象的清理相关。
PoolManager* PoolManager::getInstance()
{
if (s_singleInstance == nullptr)
{
s_singleInstance = new (std::nothrow) PoolManager();
// Add the first auto release pool
new AutoreleasePool("cocos2d autorelease pool");
}
return s_singleInstance;
}
而AutoreleasePool 在构造函数中,会将自身指针添加到PoolManager 内的管理池队列中。
这样就能通过PoolManager 来管理池队列。
AutoreleasePool::AutoreleasePool(): _name("")
{
_managedObjectArray.reserve(150);
PoolManager::getInstance()->push(this);
}
AutoreleasePool::AutoreleasePool(const std::string &name): _name(name)
{
// 初始化对象池
_managedObjectArray.reserve(150);
// 将自身添加到管理类的队列中
PoolManager::getInstance()->push(this);
}
AutoreleasePool::~AutoreleasePool()
{
CCLOGINFO("deallocing AutoreleasePool: %p", this);
clear();
PoolManager::getInstance()->pop();
}
End
|