cocos2d-x源码剖析-3-贴身女仆PoolManager

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()函数可以看出这是一个单例函数,也就是说全局只有一个实例对象。

 

std::vector _releasePoolStack

可以看出,内存池是一个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::PoolManager()
{
    _releasePoolStack.reserve(10);
}

内存池管理类一开始给出了10个空间。

 

 ~PoolManager()

PoolManager::~PoolManager()
{
    CCLOGINFO("deallocing PoolManager: %p", this);
    
    while (!_releasePoolStack.empty())
    {
        AutoreleasePool* pool = _releasePoolStack.back();
        
        delete pool;
    }
}

 ~PoolManager()不只释放了自己,还将它管理的所有内存池全部释放了。

 

destroyInstance()

销毁自身,释放内存,purgePoolManager()和此函数用法相同。因为purgePoolManager()内部只调用了destroyInstance。

purgePoolManager:

CC_DEPRECATED_ATTRIBUTE static void purgePoolManager() { destroyInstance(); }

 

getCurrentPool()

获得当前内存池,由于是栈结构所以只需要返回栈的最后一位即可。

AutoreleasePool* PoolManager::getCurrentPool() const
{
    return _releasePoolStack.back();
}

 

isObjectInPools()

判断某个对象现在是否存在内存池中。

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
};

 我们来一根一根看看这个金扫帚有什么强大的功能吧。

 

成员变量

  1.  std::vector _managedObjectArray  //存放所有对象的内存池
  2.  std::string _name //这个变量提示我们每个内存池其实是有自己的名字的
  3. bool _isClearing //判断内存池有没有被清空

 

AutoreleasePool()

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);
}

 

clear()

释放内存池中的资源。

我们先看看老版本怎么写的:

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
}

std::vector releasings创建了一个空vector,然后releasings.swap(_managedObjectArray)交换两个vector的内容,这样内存池就被清空了,那么垃圾如何释放呢?这时新建的releasings就会得到原来内存池的内容,再使用releasings来释放就行了。

for (const auto &obj : releasings)
    {
        obj->release();
    }

这样在此代码块结束的时候releasings也就释放了。

 

~AutoreleasePool()

~AutoreleasePool()中调用了clear函数,并且将当前内存池弹出栈。

AutoreleasePool::~AutoreleasePool()
{
    CCLOGINFO("deallocing AutoreleasePool: %p", this);
    clear();
    
    PoolManager::getInstance()->pop();
}

 

contains()

bool AutoreleasePool::contains(Ref* object) const
{
    for (const auto& obj : _managedObjectArray)
    {
        if (obj == object)
            return true;
    }
    return false;
}

用来判断内存池中是否包含某个指定对象。

 

addObject()

向内存池中添加对象。

void AutoreleasePool::addObject(Ref* object)
{
    _managedObjectArray.push_back(object);
}

 

总结

我们可以使用PoolManager来得到当前的AutoreleasePool,让后通过 AutoreleasePool的clear函数来清除垃圾内存。也就是女仆用扫帚扫地的意思。

cocos2d-x源码剖析-3-贴身女仆PoolManager_第1张图片

在什么时候扫地呢?

我们打开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(),也就是说游戏的主循环每执行一次,就会清除一次垃圾资源。

cocos2d-x源码剖析-3-贴身女仆PoolManager_第2张图片

你可能感兴趣的:(cocos2d-x)