欢迎转载,转载请注明原文地址: 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帮我们做了)。