自动释放池是吗,是否可以这样模仿,超简单,嘿嘿

喵聊几眼cocos2dx3.2引擎关于自动释放池里面的源码,感觉也不过如此,不知是否理解正确,这篇文章也许不正确,但完全是出于个人的理解,我可不负什么责任的。

对于自动释放池的定义,我不懂,具体还是百度下吧,以我的理解就是采用的是一种引用计数的机制,实现对同一个对象的操作多个指针的引用,然后将这个对象放到自动释放池里面,在cocos2dx3.2绘制场景的时候,遍历自动释放池里面的对象,一旦发现引用计数为1的时候就将其释放掉,为的是减少内存,提高资源利用率。哎,这表达行不行,不清楚啦。

那么什么是引用计数机制呢,怎么说呢,举个例子吧:

  #include<iostream> 

using namespace std;

#define CCLOG(strFormat, value) printf(strFormat, value) // 暂时用这个宏模仿cocos2dx3.2里面得日志打印

class Ref

{

public:

    Ref():_reference(1) {}//这里给引用计数初始化1,这是仿照cocos2dx3.2引擎里面得初始化

    virtual ~Ref(){}

    void showMsgRef(){CCLOG("This is a Ref class and its _reference:%d\n",_reference);}    

    void retain(){++_reference;} // 引用计数+1    

private:    

    int  _reference ; // 引用计数

};

好了,然后我们在main函数使用它,让多个指针只向它如下:

int main()

{

    Ref *p1 = new Ref();

    p1->showMsgRef();

    Ref *p2 = p1;

    p2->retain();

    p2->showMsgRef();    

    ..... // 这里应该还有释放资源得部分。

    return  0;

}


看到了吧,这就是同一个对象的操作多个指针的引用,例子中没有对Ref new 两次,就实现p1,p2对其操作,只是在使用的时候,调用一次retain函数就行了。这样是为了表示

有某个指针在引用它,如果再有p3,p4呢,同理可得。

那么如何释放呢,我们继续往里面添加realse函数,为了模仿CCASSERT,所以在头文件还需定义另一个宏

void Ref::release()

{

    --_reference;

    CCASSERT(_reference >0,"这引用计数应该大于等于0");

    if(_reference ==0)

    {

        delete this;

    }

}

所以目前的所用代码如下所示:

#include<iostream> 

using namespace std;

#include <assert.h>

#define CCLOG(strFormat, value) printf(strFormat, value) // 暂时用这个宏模仿cocos2dx3.2里面得日志打印


#define CCASSERT(n, msg) assert(n) // 看上面的注释

class Ref

{

public:

    Ref():_reference(1) {}//这里给引用计数初始化1,这是仿照cocos2dx3.2引擎里面得初始

    virtual ~Ref(){CCLOG("it has been destroyer%s","");}

    void showMsgRef(){CCLOG("This is a Ref class and its _reference:%d\n",_reference);}    

    void retain(){++_reference;}    

    void release();    

private:    

    int  _reference ; // 引用计数

};


void Ref::release()

{

    CCASSERT(_reference >0,"这引用计数应该大于等于0");

    --_reference;

    if(_reference ==0)

    {

        delete this;

    }

}

// 这里说说为什么CCASSERT(_reference > 0,"这引用计数应该大于0"),中的_reference > 0,其实道理很简单,因为刚开始初始化的时候,引用计数为1,是吧,如过小于1,那表明这个对象已经被删除,故断言:其必须为大于0, 不信去看下cocos2d 3.2里面的源码。

在使用的使用可以这样,在我不需要对这个对象引用了,就调用release一下,如下:

int main()

{

    Ref *p1 = new Ref();

    p1->showMsgRef();

    Ref *p2 = p1;

    p2->retain();    

    p2->showMsgRef();

    p2->release();

    p2 = nullptr;//释放候设为空,习惯而已    

    p1->release();

    p1 = nullptr;

    return  0;

}

结果如下:


看到结果了吧,后面那句表明这个对象已经被释放了。

好了,关于Ref,引用计数部分就先到这,不知理解否,呵呵,没事,多几遍,看不懂那是我的问题,因为我从来就不擅长写博客文章的,只是

现在练练而已。那么cocos2dx3.2,自动释放池又是咋回事呢,cocos2dx里面看到很多这句代码:

pRet->autorelease(); 

不着急,慢慢来。

接着我们构造自动释放池

class AutoreleasePool

{

public:

    AutoreleasePool(const std::string &name);

    void addObject(Ref *object);

    void clear();

    

private:

    std::vector<Ref*> _managedObjectArray;

    const std::string _name;

};


AutoreleasePool::AutoreleasePool(conststd::string &name)

:_name(name)

{    

    PoolManager::getInstance()->push(this);

}

void AutoreleasePool::clear()

{

    //。。。还有些代码

    for (const auto &obj : _managedObjectArray)

    {

        obj->release();

    }

    _managedObjectArray.clear();

}


void AutoreleasePool::addObject(Ref *object)

{

    _managedObjectArray.push_back(object);

}

好了,自动释放池已经建好了,接下来,我们还有建立一个自动释放池管理类,用来管理Autorelease;

class  PoolManager

{

public:

    static PoolManager* getInstance();    

    friend class AutoreleasePool;    

    void push(AutoreleasePool *pool);    

    void pop();    

private:

    PoolManager();    

    ~PoolManager();    

    static PoolManager* s_singleInstance;    

    std::vector<AutoreleasePool*> _releasePoolStack;

};


PoolManager* PoolManager::s_singleInstance =nullptr;


PoolManager *PoolManager::getInstance()

{

    if (s_singleInstance ==nullptr) {

        s_singleInstance =newPoolManager();

        newAutoreleasePool("cocos2d autorelease pool");

    }    

    returns_singleInstance;

}

void PoolManager::push(AutoreleasePool *pool)

{

    _releasePoolStack.push_back(pool);

}


void PoolManager::pop()

{

    _releasePoolStack.pop_back();

}


PoolManager是个单例,这里说说为什么在声明中加入

    friend class AutoreleasePool;    

主要是因为在PoolManager里面使用了

        new AutoreleasePool("cocos2d autorelease pool");看到了吧,呵呵。为什么要这样添加呢?嘿嘿

我们返回去看看自动释放池的构造函数:

AutoreleasePool::AutoreleasePool(conststd::string &name)

:_name(name)

{

    PoolManager::getInstance()->push(this);

}

哎,这下明白了吧,不过这里这里会遇到像以下的小问题:



为什么会这样呢,因为我出于演示的目的,将Autorelease,PoolPoolManager的定义都写在一起了,还有我忘了在

Ref类里面添加

 Ref* autorelease();

Ref* Ref::autorelease()

{    

     PoolManager::getInstance()->getCurrentPool()->addObject(this);

    return this;

}

现在加上,请在看这里又使用了PoolManager,可能也会出现类似上面的小问题,好了,我再次调整,到目前为止所有到代码如下:

#include<iostream>

using namespace std;

#include <assert.h>

#include <vector>

#define CCLOG(strFormat, value) printf(strFormat, value) // 暂时用这个宏模仿cocos2dx3.2里面得日志打印

#define CCASSERT(n, msg) assert(n) // 看上面的注释

class Ref

{

public:

    Ref():_reference(1) {}//这里给引用计数初始化1,这是仿照cocos2dx3.2引擎里面得初始

    virtual ~Ref(){CCLOG("it has been destroyer%s","");}

    void showMsgRef(){CCLOG("This is a Ref class and its _reference:%d\n", _reference);}

    

    void retain(){++_reference;}

    Ref* autorelease();

    

    void release();

    

private:

    

    int  _reference ; // 引用计数

};


class AutoreleasePool

{

public:

    AutoreleasePool(const std::string &name);

    void addObject(Ref *object);

    void clear();

    

private:

    std::vector<Ref*> _managedObjectArray;

    const std::string _name;

};



class  PoolManager

{

public:    

    static PoolManager* getInstance();    

    friend class AutoreleasePool;

    AutoreleasePool* getCurrentPool() const;    

    void push(AutoreleasePool *pool);    

    void pop();       

private:

    PoolManager();    

    ~PoolManager();    

    static PoolManager* s_singleInstance;    

    std::vector<AutoreleasePool*> _releasePoolStack;

};

Ref* Ref::autorelease()

{

    PoolManager::getInstance()->getCurrentPool()->addObject(this);

    return this;

}

void Ref::release()

{

    CCASSERT(_reference >0,"这引用计数应该大于0");

    --_reference;

    if(_reference ==0)

    {

        delete this;

    }

}


AutoreleasePool::AutoreleasePool(conststd::string &name)

:_name(name)

{

    PoolManager::getInstance()->push(this);    

}

void AutoreleasePool::clear()

{

    //。。。还有些代码

    for (const auto &obj : _managedObjectArray)

    {

        obj->release();

    }

    _managedObjectArray.clear();

}


void AutoreleasePool::addObject(Ref *object)

{

    _managedObjectArray.push_back(object);

}

PoolManager* PoolManager::s_singleInstance =nullptr;


PoolManager *PoolManager::getInstance()

{

    if (s_singleInstance ==nullptr) {

        s_singleInstance =newPoolManager();

        newAutoreleasePool("cocos2d autorelease pool");        

    }    

    returns_singleInstance;

}


PoolManager::PoolManager()

{

    _releasePoolStack.reserve(10);

}



AutoreleasePool* PoolManager::getCurrentPool()const

{

    return_releasePoolStack.back();

}


void PoolManager::push(AutoreleasePool *pool)

{

    _releasePoolStack.push_back(pool);

}


void PoolManager::pop()

{

    _releasePoolStack.pop_back();

}


到这里,基本用到的类都定义实现, 好了,先休息下, 累了吧,感觉我写了很多,不知道是否啰嗦,但为了阐述清楚,我尽量一步一步来实现。

。。。。。。十分钟过去了。。。。。

接下路,就是学会如何用啦,如何自己新建的类,然后,我将它放到自动释放池AutoreleasePool中,好吧,新建个精灵类,不过不够标准饿,滚他呢

,满足需求就ok。

class Sprite : publicRef

{

public:

    static Sprite* create(conststd::string &fileName);

private:

    bool initWithFileName(conststd::string fileName);

    Sprite(){};

   

    virtual ~Sprite(){CCLOG("the Sprite has been destroyed%s\n","");}

    

};


Sprite* Sprite::create(conststd::string &fileName)

{

    auto sprite = newSprite();

    if (sprite && sprite->initWithFileName(fileName))

    {

        sprite->autorelease(); // 嘿嘿,这里就表示添加进自动释放池了

    }

    else

    {

        delete sprite;

        sprite = nullptr;

    }

    return sprite;

}

bool Sprite::initWithFileName(conststd::string fileName)

{   

    return true;    

}

正如注释所示,需要注意的地方,那好像还有一个没解决呢?那到底在cocos2dx3.2引擎里面什么时候创建第一个自动释放池呢?

还记得在自动释放池的构造函数里面的代码么,如下:

PoolManager* PoolManager::getInstance()

{

    if (s_singleInstance ==nullptr)

    {

        s_singleInstance =newPoolManager();

        // Add the first auto release pool

        newAutoreleasePool("cocos2d autorelease pool"); // 别看了,就是这里我们在这里断电下,看看它在什么第一个创建自动释放池

    }

    returns_singleInstance;

}


好吧,我们看下图,一共三张图,有点多饿:

(一)


这是我在这里设置断点点地方,然后,我到堆栈里面返回去看,下面是第二张图:


接着,我们进入到GLView::create()函数里面去,在看,下面是第三张图:



嘿嘿,看到了吧,看到了吧,ret->autorealse();那autorelealse()里面到内容是什么呢?请看:

    PoolManager::getInstance()->getCurrentPool()->addObject(this)

终于出来了在第一次调用      PoolManager ::getInstance ()的时候,它会执行PoolManager的构造函数,也就是执行这里:

PoolManagerPoolManager::getInstance()

{

    if (s_singleInstance == nullptr)

    {

        s_singleInstance = new PoolManager();

        // Add the first auto release pool

        new AutoreleasePool("cocos2d autorelease pool");   }

    return s_singleInstance;

}

当执行到  new AutoreleasePool("cocos2d autorelease pool");   这里到时候,又调用了AutoreleasePool的构造函数,而我们再次看看AutoreleasePool的构造函数里面的情况:

AutoreleasePool::AutoreleasePool()

: _name("")

#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)

, _isClearing(false)

#endif

{

    _managedObjectArray.reserve(150);

    PoolManager::getInstance()->push(this);

}


,终于出来了,这时候就创建了第一个自动释放池,放到PoolManager的_managedObjectArray集合里面。看起来挺复杂的,不过看明白,懂得它的原理也不难,不懂?
就多调试几下啊,呵呵。
所以说在cocos2dx3.2里面在构建GLView界面多时候,就创建了第一个自动释放池,然后就凡是使用了 autorelease()的对象,都会把当前对象指针push 到当前的自动释放池当中,这第一自动释放池算是找出来了,那么它在什么时候检查里面的对象呢?答案是在绘制场景的时候,我们在AutoreleasePool的clear()函数里面断点:
又有三张图,这时候就有人可能喷啦,又有三张图,草,哎,我才不管呢,不看也行,反正我也没打算给你看滴,嘿嘿:

这上面是断点的地方。
接着跟着堆栈返回去在DisplayLinkDirector里面:

在看到了上一句了吗:drawScene(),是绘制场景啊。

在往上看,在

int Application::run()里面执行下面图的内容:




我们知道它什么时候调用 void AutoreleasePool ::clear()啦,在它里面就遍历自动释放里面的对象,逐个调用release()

void AutoreleasePool::clear()

{

    .. ...省略

    for (const auto &obj : _managedObjectArray)

    {

        obj->release();

    }

}

而在

Ref::release()里面,检查做操作,当引用计数为1的就释放掉。

void Ref::release()

{

    CCASSERT(_referenceCount >0,"reference count should greater than 0");

    --_referenceCount;

    if (_referenceCount ==0)

    {

       ....省略...

       delete this;

    }

}

是吧,没骗你吧。到这里,关于这些鸟东西前前后后,我都说了,有图有真相。

回归我的正题,既然是模仿吧,那就模仿个痛快来吧。
上面已经创建了sprite, 接着我们还需创建DisplayLinkDirector 和 Director

class DisplayLinkDirector;

class Director : Ref

{

public:

    static DisplayLinkDirector* getInstance();

    Director(){};

    virtual ~Director(){};

    void drawScene();

public:

    virtual void mainLoop() =0 ;

    

    

};


class DisplayLinkDirector : public Director

{

public:

    DisplayLinkDirector(){};


    virtual ~DisplayLinkDirector(){};


    bool initWithFileName(const std::string fileName);

public:

    virtual void mainLoop() override ;    

};


void DisplayLinkDirector::mainLoop()

{

    CCLOG("mainLoop in DisplayLinkDirector%s\n","");

    drawScene();

    PoolManager::getInstance()->getCurrentPool()->clear();    

}

void Director::drawScene()

{

    CCLOG("the drawScene in Director invoke\n","");

}

static DisplayLinkDirector *s_SharedDirector =nullptr;


DisplayLinkDirector* Director::getInstance()

{

    

    if (!s_SharedDirector)

    {

        s_SharedDirector = new (std::nothrow) DisplayLinkDirector();

    }

    

    return s_SharedDirector;

}



就是如何在xcode上用c++创建定时器,让它就像cocos2dx3.2里面每隔1 / 60.0f执行一次绘制场景,检查自动释放池里面的对象呢? 这问题先留着吧,嘿嘿。我们简单使用它,说明原理即可

我们就简单使用它
int main()
{    

  auto  sprite = Sprite::create("mm.png");  // 在这里添加 autorelease的时候,就表明创建了第一个自动释放池,并被PoolManager管理起来

  Director::getInstance()->mainLoop();     // 这里表示检查自动释放池的对象。按理来说应该创建定时器,在下一帧调用的,由于没找到xcode c++创建定时器,所有。。。

    return  0;

}

输出结果如下:

长长的一大篇,万里长征终于结束了,也许有很多地方说得不对,请大神别喷,指出,批评改正就好,感觉写博文真累,就是那么个东西,非得写一堆

东西阐明,下次吧,有经验了,我尽量简明,我会逐步有所进步的,我相信自己,所有给自己一个yes!

最后给出关于以上demo的源码下载地址:

http://download.csdn.net/detail/aiwobiezoukaicnew/8307593

关于引用计数,内存管理方面的博客文章我推荐:

http://m.blog.csdn.net/blog/tonny_guan/40790751



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值