cocos2dx 内存管理

前端之家收集整理的这篇文章主要介绍了cocos2dx 内存管理前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

欢迎转载,转载请注明原文地址: http://www.jb51.cc/article/p-oggvgroc-hh.html

本文基于cocos2d-x 3.4

一、cocos2dx内存管理基础

cocos2dx内存管理是基于引用计数的。使用了基类Ref实现类对象的引用计数记录。引擎中的所有需要管理的类都派生自Ref。(eg:Node,Sprite,Texture2D…)
首先看一下Ref的结构:
CCRef.h

  1. class CC_DLL Ref
  2. {
  3. public:
  4. void retain();
  5.  
  6. void release();
  7.  
  8. Ref* autorelease();
  9.  
  10. unsigned int getReferenceCount() const;
  11.  
  12. protected:
  13. Ref();
  14.  
  15. public:
  16. virtual ~Ref();
  17.  
  18. protected:
  19. /// count of references
  20. unsigned int _referenceCount;
  21.  
  22. friend class AutoreleasePool;
  23. };

CCRef.cpp

  1. Ref::Ref()
  2. : _referenceCount(1) // when the Ref is created,the reference count of it is 1
  3. {
  4. }
  5.  
  6. Ref::~Ref()
  7. {
  8.  
  9. }
  10.  
  11. void Ref::retain()
  12. {
  13. CCASSERT(_referenceCount > 0,"reference count should be greater than 0");
  14. ++_referenceCount;
  15. }
  16.  
  17. void Ref::release()
  18. {
  19. CCASSERT(_referenceCount > 0,"reference count should be greater than 0");
  20. --_referenceCount;
  21.  
  22. if (_referenceCount == 0)
  23. {
  24. delete this;
  25. }
  26. }
  27.  
  28. Ref* Ref::autorelease()
  29. {
  30. PoolManager::getInstance()->getCurrentPool()->addObject(this);
  31. return this;
  32. }

代码很简单,主要就是retain(),release(),autorelease(),_referenceCount和AutoreleasePool。

1.autorelease

为什么先看autorelease呢,因为autorelease是cocos2dx内存管理的根基,就是把对象放入到自动释放池(AutoreleasePool)中管理。
AutoreleasePool维护了一个vector< Ref*>的数据结构,而autorelease就是把继承自Ref的的对象放在这个vector里面管理。

2.retain

retain的作用就是:令其引用计数增1,表示获取该对象的引用权。我称之为认领,如果对象没有被认领,那么就会释放掉。在什么时候释放?
每一帧 都会遍历AutoreleasePool调用clear()

  1. void DisplayLinkDirector::mainLoop()
  2. {
  3. ...
  4. else if (! _invalid)
  5. {
  6. drawScene();
  7. // release the objects
  8. PoolManager::getInstance()->getCurrentPool()->clear();
  9. }
  10. }
  11.  
  12. void AutoreleasePool::clear()
  13. {
  14. for (const auto &obj : _managedObjectArray)
  15. {
  16. obj->release();
  17. }
  18. _managedObjectArray.clear();
  19. }

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来认领对象,你是不是没有找到,反正我最初怎么也找不到

  1. void Node::insertChild(Node* child,int z)
  2. {
  3. _transformUpdated = true;
  4. _reorderChildDirty = true;
  5. _children.pushBack(child);
  6. child->_localZOrder = z;
  7. }
  8.  
  9. void pushBack(T object)
  10. {
  11. CCASSERT(object != nullptr,"The object should not be nullptr");
  12. _data.push_back( object );
  13. object->retain();
  14. }

pushBack!!!
我还一直是标准的vector,原来是自定义的,被骗了那么久。

那么释放的时候就简单了

removeChild里面有一个_children.erase(childIndex);(当然不只一个接口,还有类似的地方)
实际上就是调用(*iter)->release();去释放了。

2.addImage

CCTextureCache里面有一个重载函数

  1. Texture2D* addImage(const std::string &filepath);
  2. Texture2D* addImage(Image *image,const std::string &key);

实现里面有一点不同,下面那个接口使用的时候

  1. _textures.insert( std::make_pair(key,texture) );
  2. texture->retain();
  3. texture->autorelease();

调用了retain和autorelease,而上面的接口并没有调用这两行代码,当时百思不得其解,现在看来,也只是放不放到AutoreleasePool而已,对它的释放来讲,并没有什么区别。因为它的释放是在

  1. void TextureCache::removeAllTextures()
  2. {
  3. for( auto it=_textures.begin(); it!=_textures.end(); ++it ) {
  4. (it->second)->release();
  5. }
  6. _textures.clear();
  7. }

在这个以及相关的函数 eg:removeUnusedTextures,removeTexture… 里面释放的。

三、结论

AutoreleasePool有什么用?简单来说,没什么用。但是引擎底层是这么写的,你又必须按着它的写法来,不然很可能报错。 好吧,还是有用的,它的用途就是防止你手动create了一个对象,但是,没有任何地方引用,也没有任何地方释放,这个时候AutoreleasePool就发挥作用了,他会帮你释放。前提是你调用了autorelease(),(create帮我们做了)。

猜你在找的Cocos2d-x相关文章