位置: 编程技术 - 正文
推荐整理分享自动释放池是吗,是否可以这样模仿,超简单,嘿嘿(自动释放池原理,本质),希望有所帮助,仅作参考,欢迎阅读内容。
文章相关热门搜索词:自动释放什么意思,自动释放池原理,本质,ios自动释放池的使用,自动释放装置,自动释放池的工作原理,自动释放池什么时候释放,自动释放池什么时候释放,自动释放池原理,本质,内容如对您有帮助,希望把文章链接给更多的朋友!
喵聊几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:%dn",_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:%dn",_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:%dn", _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();
}
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%sn","");}
};
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的构造函数,也就是执行这里:PoolManager* PoolManager::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();
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 和 Directorclass 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%sn","");
drawScene();
PoolManager::getInstance()->getCurrentPool()->clear();
}
void Director::drawScene()
{
CCLOG("the drawScene in Director invoken","");
}
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 / .0f执行一次绘制场景,检查自动释放池里面的对象呢? 这问题先留着吧,嘿嘿。我们简单使用它,说明原理即可
我们就简单使用它int main(){auto sprite = Sprite::create("mm.png"); // 在这里添加 autorelease的时候,就表明创建了第一个自动释放池,并被PoolManager管理起来
Director::getInstance()->mainLoop(); // 这里表示检查自动释放池的对象。按理来说应该创建定时器,在下一帧调用的,由于没找到xcode c创建定时器,所有。。。
return 0;
}
输出结果如下:
长长的一大篇,万里长征终于结束了,也许有很多地方说得不对,请大神别喷,指出,批评改正就好,感觉写博文真累,就是那么个东西,非得写一堆
东西阐明,下次吧,有经验了,我尽量简明,我会逐步有所进步的,我相信自己,所有给自己一个yes!
最后给出关于以上demo的源码下载地址:
解决Android客户端运行Cococs2dx编写的游戏程序遇到的意外游戏中断导致的游戏黑屏问题 今天,在Android客户端运行的游戏,按HOME键,或者是意外终止游戏以后,再次返回游戏就会出现黑屏的问题,查找了好多资料,试验了好多办法,终于发
Cocos2dx -lua QuickXDev拓展 用cocos2d-x做开发的话,用的最多的开发语言就是c/lua了,而现在公司很多都比较看重开发速度,较多的公司都选择了lua作为开发语言,同时lua的热更新也
cocos2dx 不规则按钮的实现 最近研究了一下像素级的触摸处理,有时候我们用一个不规则的图形作为一个按钮,这个不规则的图形是一张矩形的png图片,很可能图片的实际有效的
标签: 自动释放池原理,本质
本文链接地址:https://www.jiuchutong.com/biancheng/372600.html 转载请保留说明!上一篇:cocos2dx 3.2 屏幕适配的理解(cocos2dx屏幕适配解决方案)
下一篇:解决Android客户端运行Cococs2dx编写的游戏程序遇到的意外游戏中断导致的游戏黑屏问题(android遇到的难题,怎么解决的)
友情链接: 武汉网站建设