大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。
漏。
风格的内存管理是 Cocos2d-x 的一个特色。
个层次。
每一个对象维护一个引用计数器,记录该对象当前被引用的次数。
时。计数器减 1。当引用计数为 0 时,标志着该对象的生命周期结束,自己主动触发对象的回收释放。
一个程序片段必须负责任地维护引用计数。在须要维持对象生存的程序段的開始和结束分别添加和降低一次引用计数,这
样我们就能够实现十分灵活的智能内存管理了。
回收的事件转换成了使用和使用结束的事件。对于程序猿来说,维护引用计数比维护生命周期信息轻松了很多。
攻克了对象的生命周期管理问题,但堆碎片化和管理烦琐的问题仍然存在。
试图将程序猿从复杂的内存管理任务中全然解放出来。
象,然后释放其余不再须要的对象。
止内存泄露,有效地使用可用内存。
对内存堆中已经死亡的或者长时间没有使用过的对象进行清除和回收。程序猿不能手动指派垃圾回收器回收某个对象。
收机制包含分代复制垃圾回收、标记垃圾回收和增量垃圾回收。
为了实现对象的引用计数记录,
Cocos2d-x 3.0实现了新的根类Ref。 引擎中的全部类都派生自Ref。
/** Interface that defines how to clone an Ref */ class CC_DLL Clonable { public: /** returns a copy of the Ref */ virtual Clonable* clone() const = 0; /** * @js NA * @lua NA */ virtual ~Clonable() {}; /** returns a copy of the Ref. @deprecated Use clone() instead */ CC_DEPRECATED_ATTRIBUTE Ref* copy() const { // use "clone" instead CC_ASSERT(false); return nullptr; } }; class CC_DLL Ref { public: void retain(); void release(); Ref* autorelease(); unsigned int getReferenceCount() const; protected: Ref(); public: virtual ~Ref(); protected: /// count of references unsigned int _referenceCount; friend class AutoreleasePool; #if CC_ENABLE_SCRIPT_BINDING public: /// object id, ScriptSupport need public _ID unsigned int _ID; /// Lua reference id int _luaID; #endif };
Clonable类:定义如何复制Ref类的接口
virtual Clonable *clone() const =0; //克隆函数是纯虚函数,必须在子类重写例:virtual __Array *clone() const;实现:__Array* __Array::clone() const{ __Array* ret = new __Array(); ret->autorelease(); ret->initWithCapacity(this->data->num > 0 ? this->data->num : 1);return ret;}
void retain(); //保持全部权,添加Ref的引用次数
void release(); //降低引用次数,引用计数直接减一,假设引用次数等于0。马上释放
Ref* autorelease(); //自己主动释放。将该物体增加自己主动释放池。当引用计数为0时自己主动释放
举个错误使用样例:
auto obj=Node::create(); //Ref=1,可是当前Node已经在自己主动释放缓冲池中
obj->autorelease(); //错误:假设你调用autorelease()非常多次,你必须retain()
从源代码中能够看到,每一个对象包括一个用来控制生命周期的引用计数器,它就是 Ref的成员变量_referenceCount。
我们能够通过 getReferenceCount()方法获得对象当前的引用计数值。
在对象通过构造函数创建的时候,该引用值被赋为 1,表示对象由创建者所引用。
在其它地方须要引用对象时,我们会调用 retain()方法,令其引用计数增 1,表示获取该对象的引用权;
在引用 结束的时候调用 release()方法,令其引用计数值减 1,表示释放该对象的引用权。
相对于IOS SDK把这个内存计数封装到了NSAutoreleasePool中。
在cocos2d-x相同有一套CCAutoreleasePool与之相相应。
两者的使用方法基本一样。
这就要讲到这个非常有意思的方法:autorelease()。
Ref* Ref::autorelease(){ PoolManager::getInstance()->getCurrentPool()->addObject(this); return this;}
其作用是将对象放入自己主动回收池(AutoreleasePool)。
AutoreleasePool::~AutoreleasePool(){ CCLOGINFO("deallocing AutoreleasePool: %p", this); clear(); PoolManager::getInstance()->pop();}
当回收池自身被释放 的时候。 它就会对池中的全部对象运行一次 release()方法, 实现灵活的垃圾回收。
回收池能够手动创建和释放。
除此之外。 引擎在每次游戏循环開始之前也会创建一个回收池,在循环结束后释放回收池。
因此,即使我们没有手工创建和释放回收 池,每一帧结束的时候,
自己主动回收池中的对象也都会被运行一次 release()方法。
我们立即就会领略到 autorelease()的方 便之处。
以下是一个简单的样例。
能够看到。对象创建后,引用计数为 1;
运行一次 retain()后,引用计数为 2。
运行一次 release()后,引用计数回到 1;
运行一次 autorelease()后,对象的引用计数值并没有马上减 1,可是在下一帧開始前,对象会被释放掉。
以下是測试代码:
mistress = new CCSprite();mistress->init();CCLog("retainCount after init: %d", mistress->getReferenceCount());mistress->retain();CCLog("retainCount after retain: %d", mistress->getReferenceCount()); mistress->release();CCLog("retainCount after release: %d", mistress->getReferenceCount());mistress->autorelease();CCLog("retainCount after autorelease: %d", mistress->getReferenceCount());
控制台显示的日志例如以下:
Cocos2d: retainCount after init: 1
Cocos2d: retainCount after retain: 2
Cocos2d: retainCount after release: 1
Cocos2d: retainCount after autorelease: 1
我们已经知道,调用了 autorelease()方法的对象,将会在自己主动回收池释放的时候被释放一次。
尽管。Cocos2d-x 已经保证每一帧结束后释放一次回收池,并在下一帧開始前创建一个新的回收池。可是我们也应该
考虑到回收池本身维护着一个将要运行释放操作的对象列表。假设在一帧之内生成了大量的 autorelease 对象,将会导致回收池性能下降。
因此,在生成 autorelease 对象密集的区域(一般是循环中)的前后,我们最好能够手动创建并释放一个回收池。
我们能够通过回收池管理器 PoolManager的 push()或 pop()方法来创建或释放回收池。当中的 PoolManager 也是一个单例对象。能够通过PoolManager::getInstance()获取该单例对象。
在这里,再通过段简单的代码来分析自己主动回收池的嵌套机制:
PoolManager::getInstance()->push(this);for(int i=0; i<n; i++){ String* dataItem = String::createWithFormat("%d", Data[i]); stringVector->push_back(dataItem); }PoolManager::getInstance()->pop();
这段代码包括了一个运行 n 次的循环,每次都会创建一个 autorelease 对象 String。
为了保持回收池的性能,我们在循环前使用 push()方法创建了一个新的回收池。在循环结束后使用 pop()方法释放刚才创建的回收池。
不难看出,自己主动回收池是可嵌套的。
通常,引擎维护着一个回收池。全部的 autorelease 对象都加入到了这个池中。
多个自己主动回收池排列成栈结构,当我们手动创建了回收池后,回收池会压入栈的顶端。autorelease 对象仅加入到顶端的池中。当顶层的回收池被弹出释放时, 它内部全部的对象都会被释放一次, 此后出现的 autorelease 对象则会加入到下一个池中。
在自己主动回收池嵌套的情况下,每个对象是怎样增加自己主动回收池以及怎样释放的。相关代码例如以下所看到的:
//步骤 aobj1->autorelease();obj2->autorelease(); //步骤 bPoolManager::getInstance()->push(this);//步骤 cfor(int i=0; i<n; i++) { obj_array[i]->autorelease(); }//步骤 dPoolManager::getInstance()->pop();//步骤 eobj3->autorelease();
上述代码的详细过程如图所看到的:
当运行完步骤 a 时,obj1 与 obj2 被增加到回收池 1 中。如图 a 所看到的;
步骤 b 创建了一个新的回收池。此时回收池 2 接管全部 autorelease 操作,如图b 所看到的。
步骤 c 是一个循环。当中把 n 个对象增加回收池 2 中,如图c 所看到的。
步骤 d 释放了回收池 2,因此回收池 2 中的 n 个对象都被释放了一次,同一时候回收池 1 接管autorelease 操作;
步骤 e 调用 obj3 的 autorelease()方法,把 obj3 增加回收池 1 中。如图e 所看到的。
版权声明:本文博客原创文章,博客,未经同意,不得转载。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/117435.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...