欢迎转载,转载请注明原文地址: http://www.jb51.cc/article/p-oggvgroc-hh.html
本文基于cocos2d-x 3.4
一、cocos2dx内存管理基础
cocos2dx内存管理是基于引用计数的。使用了基类Ref实现类对象的引用计数记录。引擎中的所有需要管理的类都派生自Ref。(eg:Node,Sprite,Texture2D…)
首先看一下Ref的结构:
CCRef.h
class CC_DLL Ref
{
public:
void retain();
void release();
Ref* autorelease();
unsigned int getReferenceCount() const;
protected:
Ref();
public:
virtual ~Ref();
protected:
/// count of references
unsigned int _referenceCount;
friend class AutoreleasePool;
};
CCRef.cpp
Ref::Ref()
: _referenceCount(1) // when the Ref is created,the reference count of it is 1
{
}
Ref::~Ref()
{
}
void Ref::retain()
{
CCASSERT(_referenceCount > 0,"reference count should be greater than 0");
++_referenceCount;
}
void Ref::release()
{
CCASSERT(_referenceCount > 0,"reference count should be greater than 0");
--_referenceCount;
if (_referenceCount == 0)
{
delete this;
}
}
Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
代码很简单,主要就是retain(),release(),autorelease(),_referenceCount和AutoreleasePool。
1.autorelease
为什么先看autorelease呢,因为autorelease是cocos2dx内存管理的根基,就是把对象放入到自动释放池(AutoreleasePool)中管理。
AutoreleasePool维护了一个vector< Ref*>的数据结构,而autorelease就是把继承自Ref的的对象放在这个vector里面管理。
2.retain
retain的作用就是:令其引用计数增1,表示获取该对象的引用权。我称之为认领,如果对象没有被认领,那么就会释放掉。在什么时候释放?
每一帧 都会遍历AutoreleasePool调用clear()
void DisplayLinkDirector::mainLoop()
{
...
else if (! _invalid)
{
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
void AutoreleasePool::clear()
{
for (const auto &obj : _managedObjectArray)
{
obj->release();
}
_managedObjectArray.clear();
}
retain():令其引用计数增1,表示获取该对象的引用权。也就是认领对象,如果对象没有被认领,那么就会
3.release()
要真正释放的时候调用,无论是主动调用去释放,还是代码中自动遍历去释放,都需要经过这个函数,令其引用计数值减1,表示释放该对象的引用权。当引用计数为0的时候,就真正释放对象。
4.AutoreleasePool
管理自动释放的对象。(当释放池自身被释放的时候,它就会对池中的所有对象执行一次release()方法)
也就是说,如果对象没有被认领(比如addChild,我们稍后讨论),那么在每一帧drawScene之后,调用AutoreleasePool.clear()->Ref.release(),减少引用计数,然后就会释放掉没被认领的对象,如果被认领呢?那就只是把创建的时候的引用计数给减掉,然后由调用者或者引擎其他管理者管理。
AutoreleasePool的vector每次只管理一帧内创建的对象,然后drawScene()之后会clear掉。
也就是说,每一次vector里面只是存了从上一帧的_managedObjectArray.clear() 到下一帧的 _managedObjectArray.clear() 之间创建的对象。
那么被认领的对象,什么时候释放呢?我们来看看引擎中怎么用的。
二、引擎用法
1.addChild
Node里面有一个addChild函数,addChild会调用retain来认领对象,你是不是没有找到,反正我最初怎么也找不到
void Node::insertChild(Node* child,int z)
{
_transformUpdated = true;
_reorderChildDirty = true;
_children.pushBack(child);
child->_localZOrder = z;
}
void pushBack(T object)
{
CCASSERT(object != nullptr,"The object should not be nullptr");
_data.push_back( object );
object->retain();
}
pushBack!!!
我还一直是标准的vector,原来是自定义的,被骗了那么久。
那么释放的时候就简单了
removeChild里面有一个_children.erase(childIndex);(当然不只一个接口,还有类似的地方)
实际上就是调用(*iter)->release();去释放了。
2.addImage
CCTextureCache里面有一个重载函数
Texture2D* addImage(const std::string &filepath);
Texture2D* addImage(Image *image,const std::string &key);
实现里面有一点不同,下面那个接口使用的时候
_textures.insert( std::make_pair(key,texture) );
texture->retain();
texture->autorelease();
调用了retain和autorelease,而上面的接口并没有调用这两行代码,当时百思不得其解,现在看来,也只是放不放到AutoreleasePool而已,对它的释放来讲,并没有什么区别。因为它的释放是在
void TextureCache::removeAllTextures()
{
for( auto it=_textures.begin(); it!=_textures.end(); ++it ) {
(it->second)->release();
}
_textures.clear();
}
在这个以及相关的函数 eg:removeUnusedTextures,removeTexture… 里面释放的。
三、结论
AutoreleasePool有什么用?简单来说,没什么用。但是引擎底层是这么写的,你又必须按着它的写法来,不然很可能报错。 好吧,还是有用的,它的用途就是防止你手动create了一个对象,但是,没有任何地方引用,也没有任何地方释放,这个时候AutoreleasePool就发挥作用了,他会帮你释放。前提是你调用了autorelease(),(create帮我们做了)。