Refcocos2dx中的所有类的公共父类(objc中的超类的概念),说到公共父类自然是要说到Node了哦。
说到Ref自然是要讲到AutoreleasePoolPoolManager

路径

1
base/CCRef.h
base/CCRef.cpp

自动管理

cocos2dx中实现了内存的自动管理。一般情况下,我们如果要在cocos2dx中自定义类的话,最好还是继承自Ref,然后把内存的管理交给cocos2dx吧。

这里看一段Sprite的类方法

1
2
3
4
5
6
7
8
9
10
11
12
Sprite* Sprite::create()
{
auto pSprite = new Sprite();
if (pSprite && pSprite->init())
{
pSprite->autorelease();
return pSprite;
}

CC_SAFE_DELETE(pSprite);
return nullptr;
}

cocos2dx内置类的创建方法会自动把创建好的实例加入自动管理。

源码分析

先贴上头文件,这里省去了脚本语言相关的东西,以后搞lua的时候再开一篇文章记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#ifndef __CCREF_H__
#define __CCREF_H__

#include "CCPlatformMacros.h"
#include "ccConfig.h"

NS_CC_BEGIN

class CC_DLL Ref
{
public:
/* 增加引用计数 */
void retain();

/* 减小引用计数 */
void release();

/* 将该对象加入内存自动回收管理 */
Ref* autorelease();

unsigned int getReferenceCount() const;

protected:
Ref();

public:
virtual ~Ref();

protected:
/* 引用计数 */
unsigned int _referenceCount;
friend class AutoreleasePool;
};

NS_CC_END

#endif // __CCREF_H__

从头文件可知

  • AutorelasePoolRef的友元函数
  • cocos2dx通过维护引用计数来实现内存的自动管理

这里的cpp文件中的脚本语言相关的代码我就省去了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Ref::Ref() : _referenceCount(1) { }

Ref::~Ref() { }

void Ref::retain()
{
CCASSERT(_referenceCount > 0, "reference count should greater than 0");
++_referenceCount;
}

void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should greater than 0");
--_referenceCount;

if (_referenceCount == 0)
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
/* 开启调试模式下的话,会对该对象进行检查 */
/* 检查是否还在PoolManager的管理之下 */
/* 若还在管理之下的话,则引发一个断言 */
auto poolManager = PoolManager::getInstance();
if (!poolManager->getCurrentPool()->isClearing() && poolManager->isObjectInPools(this))
{
CCASSERT(false, "The reference shouldn't be 0 because it is still in autorelease pool.");
}
#endif
delete this;
}
}

Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}

unsigned int Ref::getReferenceCount() const { return _referenceCount; }

通过阅读源码,我们可以知道

  • 通过构造函数可知,创建的时候引用计数为1
  • retain只是单单的增加1个引用计数
  • release减少1个引用计数,若引用计数为0,则释放资源。
  • autorelease把该对象加入PoolManager的自动管理之中

用法

当我们需要预加载一些资源的时候,可以先在游戏开始的时候,加载到内存当中,然后调用retain增加引用计数。
这样可以保证我们的资源不会被cocos2dx给自动回收掉。

错误的用法

这里的错误用法,取自源码里面的注释(=。=)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 错误用法 1 */
auto obj = Node::create();
/* 由于该对象是一个自动管理的引用计数为1的对象 */
/* 调用autorelease会再次加入内存自动管理 */
/* 由于cocos2dx的内存自动管理会定期清理一次 */
/* 那么现在这个obj对象在cocos2dx每次清理的时候会调用两次release */
obj->autorelease();

/* 错误用法 2 */
auto obj = Node::create();
/* 由于该对象是一个自动管理的引用计数为1的对象 */
/* 调用release则会直接释放对象。*/
/* 在调试模式下,会引发断言 */
obj->release();

总结

这篇文章只是单单分析了Ref这个类,cocos2dx这个内存自动管理的机制,看上去还蛮简单的。