8、Cocos2dx 3.0三,找一个小游戏开发3.0存储器管理的版本号

重开发人员的劳动成果,转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/27693365

复杂的内存管理

移动设备上的硬件资源十分有限,内存尤为宝贵。开发人员必须十分谨慎地利用内存,避免不必要的消耗。更要防止内存泄漏。

基于 Cocos2d-iPhone 的 Objective-C风格的内存管理是 Cocos2d-x 的一个特色。

把 Objective-C 的内存管理方式引入 C++,使得游戏开发的内存管理难度下降了个层次。

内存管理一直是一个非常复杂且不易处理的问题,开发人员必须充分考虑分配回收的方式、

以及分配回收内存的时机,针对堆和栈做不同的优化处理。

内存管理的核心是动态分配的对象所使用的内存必须保证在使用完成后有效地释放掉,

即面向对象语言中的管理对象的生命周期。

C++中变量的内存空间的分配问题,我们在c++中写一个类,能够在栈上分配内存空间也能够使用new在堆上分配内存空间。假设类对象是在栈上分配的内存空间。这个内存空间的管理就不是我们的事了,但假设是在堆上分配的内存空间,当然须要我们来手动的delete了!

因为 C++是一个较为底层 的语言。其设计上不包括不论什么智能管理内存的机制。

一个对象在使用完成后必须被回收。然而在复杂的程序中,对象全部权在不同程序片段间传递或共享,

使得确定回收的时机十分困难,因此内存管理成为了程序猿十分头疼的问题。

还有一方面,过于零散的对象分配回收可能导致堆中的内存碎片化,减少内存的使用效率。

因此。我们须要一个合适的机制来缓解这个问题。

比方cocos2dx採用的是在堆上分配内存空间,既然在堆上分配内存空间,那么怎样管理这个内存空间,什么时候应该释放就是个问题了!在程序中,当我们创建了一个对象的时候。这块内存空间常常是被不同的对象引用。假设删除的早了。有对象还在引用这块内存空间那么程序必定要崩溃!

以下看cocos2dx是怎么解决的?

眼下现有的智能内存管理技术

眼下,主要有两种实现智能管理内存的技术。一是引用计数,一是垃圾回收。

引用计数:它是一种非常有效的机制,通过给每一个对象维护一个引用计数器,记录该对象当前被引用的次数。

当对象添加一次引用时,计数器加 1;而对象失去一次引用时。计数器减 1。当引用计数为 0 时,标志着该对象的生命周期结束,自己主动触发对象的回收释放。

引用计数的重要规则是每一个程序片段必须负责任地维护引用计数。在须要维持对象生存的程序段的開始和结束分别添加和降低一次引用计数,这样我们就能够实现十分灵活的智能内存管理了。

实际上。这与 new 和 delete 的配对使用十分类似。可是非常巧妙地将生成和回收的事件转换成了使用和使用结束的事件。对于程序猿来说,维护引用计数比维护生命周期信息轻松了很多。

引用计数攻克了对象的生命周期管理问题,但堆碎片化和管理烦琐的问题仍然存在。

垃圾回收:它通过引入一种自己主动的内存回收器,试图将程序猿从复杂的内存管理任务中全然解放出来。

它会自己主动跟踪每个对象的全部引用,以便找到全部正在使用的对象,然后释放其余不再须要的对象。

垃圾回收器还能够压缩使用中的内存,以缩小堆所须要的工作空间。

垃圾回收能够防止内存泄露,有效地使用可用内存。

可是。垃圾回收器一般是作为一个单独的低级别的线程执行的,在不可预知的情况下对内存堆中已经死亡的或者长时间没有使用过的对象进行清除和回收。程序猿不能手动指派垃圾回收器回收某个对象。

回收机制包含分代复制垃圾回收、标记垃圾回收和增量垃圾回收。

Cocos2d-x使用的内存管理机制

Cocos2d-x 来源于 Cocos2d-iPhone,因此为了与 Objective-C 一致,Cocos2d-x 也採用了引用计数与自己主动回收的内存管理机制为了实现对象的引用计数记录, Cocos2d-x
3.0实现了新的根类Ref。 引擎中的全部类都派生自Ref。

在"cocos/base/CCRef.h" 头文件里我们能够看到 Clonable类和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;
}

Ref类

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 对象则会加入到下一个池中。

在自己主动回收池嵌套的情况下,每个对象是怎样增加自己主动回收池以及怎样释放的。相关代码例如以下所看到的:

//步骤 a
obj1->autorelease();
obj2->autorelease(); 

//步骤 b
PoolManager::getInstance()->push(this);

//步骤 c
for(int i=0; i<n; i++) {
 obj_array[i]->autorelease();
 }
//步骤 d
PoolManager::getInstance()->pop();
//步骤 e
obj3->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 所看到的。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGFuaWVsenp1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" >

郝萌主友情提示:

cocos2d-x引擎给开发人员提供了极极大的方便。哦,但不要乱用、、、

版权声明:本文博客原创文章,博客,未经同意,不得转载。

时间: 2024-10-11 22:37:12

8、Cocos2dx 3.0三,找一个小游戏开发3.0存储器管理的版本号的相关文章

14、Cocos2dx 3.0三,找一个小游戏开发Scene and Layer:游戏梦想

发人员的劳动成果,转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/30474393 Scene :场景 了解了Director 之后,接下来介绍 Scene 这个与它紧密相关的游戏组件. 通过之前的学习,我们已经了解了场景以及它在流程控制中的地位. 在 Cocos2d-x 中,Scene 定义了一个场景.场景仅仅是层的容器.包括了全部须要显示的游戏元素. 因此相对于其它游戏元素,Scene 并没有提供什么特别的功能,就是一

4、Cocos2dx 3.0三,找一个小游戏开发Hello World 分析

尊重开发人员的劳动成果.转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/27186557 Hello World 分析 打开新建的"findmistress"项目,能够看到项目文件是由多个代码文件及目录组成的.当中 Hello World 的代码文件直接存放于该项目目录中.以下我们来具体介绍一下项目的文件组成. 1."resource" 该目录主要用于存放游戏中须要的图片.音频和配置等资源文件

linux下开发,解决cocos2d-x中编译出现的一个小问题, undefined reference to symbol &amp;#39;[email&#160;protected]@GLIBC_2.2.5&amp;#39;

解决cocos2d-x中编译出现的一个小问题 对于cocos2d-x 2.×中编译中,若头文件里引入了#include "cocos-ext.h",在进行C++编译的时候会遇到例如以下错误: undefined reference to symbol '[email protected]@GLIBC_2.2.5'/lib/x86_64-linux-gnu/libpthread.so.0: error adding symbols: DSO missing from command li

DirectX游戏开发——从一个小游戏开始

本系列文章由birdlove1987编写,转载请注明出处. 文章链接: http://blog.csdn.net/zhurui_idea/article/details/26364129 写在前面:自己对DirectX挺感兴趣的,加上自己目前在研究3D重建方面的东西,所以利用课余时间学习一下.看了一段时间的书,感觉还是靠动手编写一些小例子来学习,进步的更快体会的更深.所以从我自己写的一个小游戏开始吧,把自己学习心得和自己的一些想法写下来.更是欢迎有兴趣的童鞋来和我交流. 首先:先把我的小例子分享

微信为什么要搞一个小游戏?

众厂关于 H5 游戏市场的争夺,自 2014 年以来,一直就没有停止过.但一直也没有尘埃落定,最终花落谁家,鹿死谁手现在还不好讲.微信推出小程序,继而推出小游戏,只是腾讯数年来在 H5 市场企业战略的一环. 微信为什么要自定义一套技术,起名为小程序?明明就是原来的 H5 技术,为什么 CSS 不叫 CSS,叫 WXSS:为什么 HTML 不叫 HTML,叫 WXML:为什么明明有 JS.TS,还要自主研发一个 WXS,并且声称"WXS 与 JS 是不同的语言,有自己的语法,并不和 JS 一致&q

使用AxureRP7.0制作经典数独小游戏原型,axure游戏原型下载

之前,有同学在Q群中提问,如何使用axure制作经典数独解谜小游戏,当时由于时间关系没有来得及亲手制作,而是给同学们提供了Axure6.5版本的一个数独解谜游戏的原型,本教程由axure原型库网站录制,转载请注明出处!但是那个原型做的太过繁杂,所以仅供大家参考交流:在此,金乌老师特地抽时间给同学们使用AxureRP7.0制作了一下,感觉对实战逻辑分析和axure变量的掌握比较有考验,所以就放出来供大家学习交流使用. 在学习的过程中,如果你仅凭自己现有的对axure的掌握,无法准确分析并组织出原型

练手WPF(三)——扫雷小游戏的简易实现(中)

原文:练手WPF(三)--扫雷小游戏的简易实现(中) 八.随机布雷 /// <summary> /// 随机布地雷 /// </summary> /// <param name="mineNum">地雷数</param> private void SetRndMine(int mineNum) { for (int k = 0; k < mineNum; k++) { int nullnum = 0; for (int j = 0;

基于Cocos2d-x-1.0.1的飞机大战游戏开发实例(下)

在飞机大战游戏开发中遇到的问题和解决方法: 1.在添加菜单时,我要添加一个有背景的菜单,需要在菜单pMenu中添加一个图片精灵,结果编译过了但是运行出错,如下图: 查了很多资料,调试了很长时间,整个人都要崩溃了. 最后发现引擎中CCMenu::itemForTouch函数中有遍历子节点的行为,但是循环中没有判断子节点类型是否为CCMenuItem.如图:码,这样一来,加入到pMenu中的图片精灵被当作菜单项取了出来使用,导致报错.老版本的果然又不完善的地方,整个人都不好了...果断修改引擎里的源

基于Cocos2d-x-1.0.1的飞机大战游戏开发实例(中)

接<基于Cocos2d-x-1.0.1的飞机大战游戏开发实例(上)> 三.代码分析 1.界面初始化 1 bool PlaneWarGame::init() 2 { 3 bool bRet = false; 4 do 5 { 6 CC_BREAK_IF(! CCLayer::init()); 7 8 _size = CCDirector::sharedDirector()->getWinSize(); 9 10 // 设置触摸可用 11 this->setIsTouchEnabled