Cocos2dx内存管理机制

cocos2dx里的所有节点对象都继承自Ref
下面是Ref的源代码核心部分摘录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Ref
{
public:
void retain();
void release();
Ref * autorelease();
unsigned int getReferenceCount() const;
protected:
Ref();
public:
virtual ~Ref();
protected:
unsigned int _referenceCount;
friend class AutoreleasePool;
}
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
Ref::Ref(): _referenceCount(1){}
Ref::~Ref() {}
void Ref::retain()
{
assert(_referenceCount > 0, "reference count should be greater than 0");
++_referenceCount;
}
void Ref::release()
{
assert(_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;
}
unsigned int Ref::getReferenceCount() const
{
return _referenceCount;
}

可以看到Ref主要就是运用了引用计数,在一个节点的create函数中通常都会都会调用autorelease,在这个函数中调用的是AutoreleasePool的addObject方法。
下面是AutoreleasePool以及PoolManager的源代码实现核心部分摘录

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
class AutoreleasePool
{
public:
AutoreleasePool();//warning Don't create an autorelease pool in heap, create it in stack.
AutoreleasePool(const std::string &name);
~AutoreleasePool();
void addObject();
void clear();
private:
std::vector<Ref *> _managedObjectArray;
std::string _name;
}
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()
{
clear();
PoolManager::getInstance()->pop();
}
void AutoreleasePool::addObject(Ref * object)
{
_managedObjectArray.push_back(object);
}
void AutoreleasePool::clear()
{
std::vector<Ref*> releasings;
releasings.swap(_managedObjectArray);
for (const auto &obj : releasings)
{
obj->release();
}
}

class PoolManager
{
public:
static PoolManager * getInstance();
AutoreleasePool * getCurrentPool() const;
friend class AutoreleasePool;
private:
PoolManager();
~PoolManager();
void push(AutoreleasePool * pool);
void pop();
static PoolManager * s_singleInstance;
std::vector<AutoreleasePool *> _releasePoolStack;
}
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;
}
PoolManager::PoolManager()
{
_releasePoolStack.reserve(10);
}
PoolManager::~PoolManager()
{
while(!_releasePoolStack.empty())
{
AutoreleasePool * pool = _releasePoolStack.back();
delete pool;
}
}
PoolManager::getCurrentPool() const
{
return _releasePoolStack.back();
}
void PoolManager::push(AutoreleasePool * pool)
{
_releasePoolStack.push_back(pool);
}
void PoolManager::pop()
{
assert(!_releasePoolStack.empty());
_releasePoolStack.pop_back();
}

总结:
1.autorelease()的实质是将对象加入自动释放池,对象的引用计数不会立刻减1,在自动释放池被回收时对象执行release()。
2.autorelease()只有在自动释放池被释放时才会进行一次释放操作,如果对象释放的次数超过了应有的次数,则这个错误在调用autorelease()时并不会被发现,只有当自动释放池被释放时(通常也就是游戏的每一帧结束时),游戏才会崩溃。在这种情况下,定位错误就变得十分困难了。例如,在游戏中,一个对象含有1个引用计数,但是却被调用了两次autorelease()。在第二次调用autorelease()时,游戏会继续执行这一帧,结束游戏时才会崩溃,很难及时找到出错的地点。因此,我们建议在开发过程中应该避免滥用autorelease(),只在工厂方法等不得不用的情况下使用,尽量以release()来释放对象引用。
3.autorelease()并不是毫无代价的,其背后的释放池机制同样需要占用内存和CPU资源。过多的使用autorelease()会增加自动释放池的管理和释放池维护对象存取释放的支出。在内存和CPU资源本就不足的程序中使得系统资源更加紧张。此时就需要我们合理创建自动释放池管理对象autorelease。
不用的对象推荐使用release()来释放对象引用,立即回收。