PoolManager
上一节我们说Cocos2d是一颗大树的话,Ref类就是这棵大树的根,那么为了这颗大树的健康我们需要给它浇水除虫,在程序中,这些繁重的工作都要交给贴身女仆PoolManager去做。
PoolManager是Cocos2d最最要的内存池管理类,它管理着AutoreleasePool内存池,它可以将垃圾资源释放,提高整个程序的性能。
class CC_DLL PoolManager
{
public:
CC_DEPRECATED_ATTRIBUTE static PoolManager* sharedPoolManager() { return getInstance(); }
static PoolManager* getInstance();
CC_DEPRECATED_ATTRIBUTE static void purgePoolManager() { destroyInstance(); }
static void destroyInstance();
AutoreleasePool *getCurrentPool() const;
bool isObjectInPools(Ref* obj) const;
friend class AutoreleasePool;
private:
PoolManager();
~PoolManager();
void push(AutoreleasePool *pool);
void pop();
static PoolManager* s_singleInstance;
std::vector _releasePoolStack;
};
看到程序中有pop,push操作我们可以大概看出这是一个栈数据结构。将构造函数和析构函数声明为私有,并且定义了getInstance()函数可以看出这是一个单例函数,也就是说全局只有一个实例对象。
可以看出,内存池是一个AutoreleasePool指针类型的链表,我们可以看看pop,push操作
pop
void PoolManager::pop()
{
CC_ASSERT(!_releasePoolStack.empty());
_releasePoolStack.pop_back();
}
push
void PoolManager::push(AutoreleasePool *pool)
{
_releasePoolStack.push_back(pool);
}
pop,posh操作只是封装了vector函数的pop_back和push_back函数。
PoolManager::PoolManager()
{
_releasePoolStack.reserve(10);
}
内存池管理类一开始给出了10个空间。
PoolManager::~PoolManager()
{
CCLOGINFO("deallocing PoolManager: %p", this);
while (!_releasePoolStack.empty())
{
AutoreleasePool* pool = _releasePoolStack.back();
delete pool;
}
}
~PoolManager()不只释放了自己,还将它管理的所有内存池全部释放了。
销毁自身,释放内存,purgePoolManager()和此函数用法相同。因为purgePoolManager()内部只调用了destroyInstance。
purgePoolManager:
CC_DEPRECATED_ATTRIBUTE static void purgePoolManager() { destroyInstance(); }
获得当前内存池,由于是栈结构所以只需要返回栈的最后一位即可。
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;
}
由于PoolManager存放了所有的AutoreleasePool对象,所以只要遍历PoolManager内的内存池,并判断这些内存池是否含有传入的对象就行了,pool->contains(obj)中的contains()是AutoreleasePool的一个成员函数,它用来判断内存池中是否包含传入的对象。
小结
就这?看完上面的代码以后,你一定有这样的疑惑,我的回答是:就这!贴心女仆只有这些功能,手里掌管着所有内存池的权限,可以任意操控他们,当然想让PoolManager女仆发挥最大的威力,你得把她的扫帚给她啊。
AutoreleasePool ——女仆的金扫帚
class CC_DLL AutoreleasePool
{
public:
AutoreleasePool();
AutoreleasePool(const std::string &name);
~AutoreleasePool();
void addObject(Ref *object);
void clear();
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
bool isClearing() const { return _isClearing; };
#endif
bool contains(Ref* object) const;
void dump();
private:
std::vector _managedObjectArray;
std::string _name;
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
bool _isClearing;
#endif
};
我们来一根一根看看这个金扫帚有什么强大的功能吧。
AutoreleasePool()用来初始化内存池,初始化150个空间,并将自己添加到PoolManager的栈中,同时设置_isClearing为false,指定_name为空字符串,AutoreleasePool()有一个重载,它含有std::string参数,用来指定初始化_name的值。
AutoreleasePool():
AutoreleasePool::AutoreleasePool()
: _name("")
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
, _isClearing(false)
#endif
{
_managedObjectArray.reserve(150);
PoolManager::getInstance()->push(this);
}
AutoreleasePool(const std::string &name):
AutoreleasePool::AutoreleasePool(const std::string &name)
: _name(name)
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
, _isClearing(false)
#endif
{
_managedObjectArray.reserve(150);
PoolManager::getInstance()->push(this);
}
释放内存池中的资源。
我们先看看老版本怎么写的:
void AutoreleasePool::clear()
{
for (const auto &obj : _managedObjectArray)
{
obj->release();
}
_managedObjectArray.clear();
}
在老版本中会先遍历整个数组,让每一个对象都release一次,然后再调用clear()函数清除数组。他的作用在哪里呢,我们知道在引用计数中release会让引用计数减一,如果引用计数减小到0的时候,它将被释放,而AutoreleasePool::clear()函数使得每一个对象都release一次,它的作用就在于,把你创建了但是没有使用的垃圾内存释放,这样就起到了清除垃圾的效果,但是,这个代码再仔细看看,写的也太糟糕了,虽然使用release释放了Ref对象的垃圾,但是自身却使用了vector::clear()函数来清除数组,我们知道vector调用clear之后, vector的尺寸(size)将变成0. 但它的容量却并不发生变化, vector本身并不释放任何内存。这样一来又增加自己产生的垃圾了。不过在最新版本中已经将这个糟糕的写法的改进了。
船新版本:
void AutoreleasePool::clear()
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = true;
#endif
std::vector releasings;
releasings.swap(_managedObjectArray);
for (const auto &obj : releasings)
{
obj->release();
}
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = false;
#endif
}
for (const auto &obj : releasings)
{
obj->release();
}
这样在此代码块结束的时候releasings也就释放了。
~AutoreleasePool()中调用了clear函数,并且将当前内存池弹出栈。
AutoreleasePool::~AutoreleasePool()
{
CCLOGINFO("deallocing AutoreleasePool: %p", this);
clear();
PoolManager::getInstance()->pop();
}
bool AutoreleasePool::contains(Ref* object) const
{
for (const auto& obj : _managedObjectArray)
{
if (obj == object)
return true;
}
return false;
}
用来判断内存池中是否包含某个指定对象。
向内存池中添加对象。
void AutoreleasePool::addObject(Ref* object)
{
_managedObjectArray.push_back(object);
}
总结
我们可以使用PoolManager来得到当前的AutoreleasePool,让后通过 AutoreleasePool的clear函数来清除垃圾内存。也就是女仆用扫帚扫地的意思。
在什么时候扫地呢?
我们打开Director类的文件CCDirector.h,我们可以找到一个mainLoop函数,来看看他的实现:
void Director::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (_restartDirectorInNextLoop)
{
_restartDirectorInNextLoop = false;
restartDirector();
}
else if (! _invalid)
{
drawScene();
// 这里释放了垃圾资源
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
也就是说每次mainLoop执行就会释放一次资源,还记得main.c里面的main函数return了一个特殊的东西吗?
return Application::getInstance()->run();
这个run就是游戏的主循环,在主循环里使用了director->mainLoop(),也就是说游戏的主循环每执行一次,就会清除一次垃圾资源。