Coco2d-x内存管理包括3个类,Ref(引用计数类)、AutoreleasePool(自动释放池)、PoolManager(自动释放池管理类)。
- Ref:Ref是所有需要内存管理的类的基类,用于管理对象的引用计数。Ref中包含两种内存管理函数:手动管理函数retain()(引用计数加1)和release()(引用计数减1,当引用计数为0时,deleted this释放自身),自动管理类函数autorelease()(将对象自身交与PoolManager的栈顶AutoreleasePool管理,PoolManager会每帧都会release()一次所有autorelease对象)。
- AutoreleasePool:AutoreleasePool是用于管理autorelease对象的管理类。本身是一个Vector,最少可以装150个对象(初始化reverse()了150个elements)。Ref中autorelease()调用的是PoolManager的栈顶的AutoreleasePool对象中的addObject()函数,将对象自身加入AutoreleasePool。PoolManager中每帧调用的是栈顶的AutoreleasePool对象的clear()函数release()一次全部autorelease对象(只是引用计数减1,不一定会delete)。
- PoolManager:PoolManager是用于管理AutoreleasePool的一个管理类,它是一个单实例类,初始化可以包含10个池,其自身也是一个Vector,当作栈使用,一般来说PoolManager会用到的函数就只有getCurrentPool(),返回的是栈顶即Vector最后一个AutoreleasePool,因为一般来说只用一个AutoreleasePool就可以了,所以push(AutoreleasePool *pool)和pop()基本不会使用。
操作流程(以Scene为例子)
1.假设要新建一个场景:auto scene = Scene::create()
//--------------------------------------------------------------------
//
// Scene::create()
//
//--------------------------------------------------------------------
Scene* Scene::create()
{
Scene *ret = new (std::nothrow) Scene();
if (ret && ret->init())
{
ret->autorelease();
return ret;
}
else
{
CC_SAFE_DELETE(ret);
return nullptr;
}
}
在new一个Scene之后,调用该Scene的autorelease()函数,将该对象加入到自动释放池中
2.ret->autorelease();
//--------------------------------------------------------------------
//
// Ref:::autorelease()
//
//--------------------------------------------------------------------
Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
该Scene的autorelease()函数中,第一次调用PoolManager的getInstance(),需要进行PoolManager的初始化
3.PoolManager初始化:PoolManager::getInstance()
//--------------------------------------------------------------------
//
// PoolManager:::getInstance()
//
//--------------------------------------------------------------------
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;
}
初始化包括:new一个PoolManager和new一个名为cocos2d autorelease pool的默认AutoreleasePool。
//--------------------------------------------------------------------
//
// PoolManager:::PoolManager()
//
//--------------------------------------------------------------------
PoolManager::PoolManager()
{
_releasePoolStack.reserve(10);
}
new一个PoolManager会申请一个至少可以保存10个AutoreleasePool的数组并当作栈来使用,其实一般只需要用到1个AutoreleasePool
//--------------------------------------------------------------------
//
// AutoreleasePool:::AutoreleasePool()
//
//--------------------------------------------------------------------
AutoreleasePool::AutoreleasePool(const std::string &name)
: _name(name)
{
_managedObjectArray.reserve(150);
PoolManager::getInstance()->push(this);
}
new一个AutoreleasePool会申请一个至少可以保存150个Ref类对象的数组,并把自身push到PoolManager的自动释放池栈的栈顶
//--------------------------------------------------------------------
//
// PoolManager:::push()
//
//--------------------------------------------------------------------
void PoolManager::push(AutoreleasePool *pool)
{
_releasePoolStack.push_back(pool);
}
一般来说,PoolManager只会push一个默认的AutoreleasePool,不再需要其他的AutoreleasePool,所以PoolManager的自动释放池栈中一般只有一个elements
4.初始化PoolManager之后,获取栈顶的AutoreleasePool:PoolManager::getInstance()->getCurrentPool()
//--------------------------------------------------------------------
//
// AutoreleasePool:::getCurrentPool()
//
//--------------------------------------------------------------------
AutoreleasePool* PoolManager::getCurrentPool() const
{
return _releasePoolStack.back();
}
因为Vector当作栈使用,所以_releasePoolStack的尾部就是栈顶,返回尾部_releasePoolStack.back()
5.获取当前AutoreleasePool后,把该Scene加入AutoreleasePool: PoolManager::getInstance()->getCurrentPool()->addObject(this);
//--------------------------------------------------------------------
//
// AutoreleasePool:::addObject()
//
//--------------------------------------------------------------------
void AutoreleasePool::addObject(Ref* object)
{
_managedObjectArray.push_back(object);
}
把该Scene加入_managedObjectArray尾部
6.autorelease对象会在mainLoop()中调用PoolManager::getInstance()->getCurrentPool()->clear()进行内存清理
//--------------------------------------------------------------------
//
// DisplayLinkDirector:::mainLoop()
//
//--------------------------------------------------------------------
void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (_restartDirectorInNextLoop)
{
_restartDirectorInNextLoop = false;
restartDirector();
}
else if (! _invalid)
{
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
在draw所有的scene之后(一帧所有的rendering后),进行内存清理
//--------------------------------------------------------------------
//
// AutoreleasePool:::clear()
//
//--------------------------------------------------------------------
void AutoreleasePool::clear()
{
std::vector<Ref*> releasings;
releasings.swap(_managedObjectArray);
for (const auto &obj : releasings)
{
obj->release();
}
}
这里用到swap()函数releasings.swap(_managedObjectArray),大概就像交换两个vector的保存数据的内存首地址,然后AutoreleasePool中的_managedObjectArray的保存数据的内存首地址指向releasings的保存数据的内存首地址,两个vector中的内容交换后_managedObjectArray空了,releasings需要手动清空,即遍历releasings调用elements的release()进行内存清理。
7.swap()的使用方法
vector<int> vec(100000, 0);
cout << "vec[0] = " <<vec[0] << " ";
cout << "vec[100000 - 1] = " << vec[100000 - 1] << endl;
for (int i = 0; i < 100000 - 2; ++i) vec.pop_back();
cout << "After pop_back() (100000 - 2) times , vec.capacity() = " << vec.capacity() << endl;
vector<int>(vec).swap(vec);//vector<int>(vec)是用vec作为形参新建一个vector,此时vec只剩两个elements了,所以新建的vector容量为2
cout << "After swap() with vector<int>(vec) (a new vector only have 2 elements), vec.capacity() = " << vec.capacity() << endl;
三个类的所有函数
//--------------------------------------------------------------------
//
// Ref
//
//--------------------------------------------------------------------
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;
}
unsigned int Ref::getReferenceCount() const
{
return _referenceCount;
}
//--------------------------------------------------------------------
//
// AutoreleasePool
//
//--------------------------------------------------------------------
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()
{
CCLOGINFO("deallocing AutoreleasePool: %p", this);
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();
}
}
bool AutoreleasePool::contains(Ref* object) const
{
for (const auto& obj : _managedObjectArray)
{
if (obj == object)
return true;
}
return false;
}
//--------------------------------------------------------------------
//
// PoolManager
//
//--------------------------------------------------------------------
PoolManager* PoolManager::s_singleInstance = nullptr;
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;
}
void PoolManager::destroyInstance()
{
delete s_singleInstance;
s_singleInstance = nullptr;
}
PoolManager::PoolManager()
{
_releasePoolStack.reserve(10);
}
PoolManager::~PoolManager()
{
CCLOGINFO("deallocing PoolManager: %p", this);
while (!_releasePoolStack.empty())
{
AutoreleasePool* pool = _releasePoolStack.back();
delete pool;
}
}
AutoreleasePool* PoolManager::getCurrentPool() const
{
return _releasePoolStack.back();
}
bool PoolManager::isObjectInPools(Ref* obj) const
{
for (const auto& pool : _releasePoolStack)
{
if (pool->contains(obj))
return true;
}
return false;
}
void PoolManager::push(AutoreleasePool *pool)
{
_releasePoolStack.push_back(pool);
}
void PoolManager::pop()
{
CC_ASSERT(!_releasePoolStack.empty());
_releasePoolStack.pop_back();
}