加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Cocos2d-x内存管理

发布时间:2020-12-14 16:29:22 所属栏目:百科 来源:网络整理
导读:Cocos2d-x内存管理浅解 1、首先我们知道内存管理分为c++自身管理机制以及Cocos2d-x内存管理机制。在c++中,内存分为堆区、栈区、静态存储区(全局存储区)、常量存储区、自由存储区。 主要先说一下堆区和栈区。堆区主要由new和malloc分配,new与delete,mall

Cocos2d-x内存管理浅解

1、首先我们知道内存管理分为c++自身管理机制以及Cocos2d-x内存管理机制。在c++中,内存分为堆区、栈区、静态存储区(全局存储区)、常量存储区、自由存储区。

主要先说一下堆区和栈区。堆区主要由new和malloc分配,new与delete,malloc与free成对出现,保证内存的分配与回收。堆内存分配地址是逐渐增大的,这一点与栈区相反,我们都知道栈是先进后出,所以栈的存储方向是内存地址逐渐减小的。栈中的内存也是系统自动回收的,这个我们不需要考虑自己管理内存泄露的问题,而堆区的释放一定要注意使用完后手动释放,不然就会出现内存泄露的问题。在小的程序中我们可能感受不到这个问题的严重性,在成品软件中我们必须要严防这个问题,很多时候在PC端开发的时候没有感觉到任何问题,但是版本发布后就会发现出现很多Crash,这个其中很重要的原因就是因为内存泄露。我们经常说手机内存的大小,当手机装了一大堆软件之后就会变得非常卡,就是这个原因,内存泄露之后正在运行的程序越来越感觉内存不够用,严重的就是不断卡顿然后Crash。所以要养成习惯,注意内存使用与回收的问题。

另外关于静态存储区的问题,我们需要注意几点,静态函数与静态成员变量是类所拥有的不是某个对象所独有的,静态成员可以由类名调用,统一赋值或改变,这是静态成员的优点同样也是缺点,当我们需要一个全局变量时我们一般会使用静态变量,使用十分方便但是当不同的对象需要对其改变而其他对象不需要时则会发生数据不稳定的情况,这是我们不愿意看到的。对于其优点我们一般在需要全局出现的地方使用,例如单例模式中,我们需要一个单例类,单例类对象的初始化函数就是静态成员,表明全局性,而我们知道静态函数只能操作静态数据,所以我们的成员指针也必须是静态的。这样我们就实现了对象的唯一性。
2、cocos2d-x内存管理机制

cocos2d-x内存管理总的原则是谁拥有 , 谁管理 , 谁释放。

cocos2d-x使用引用计数对内存进行管理。当我们在堆上分配一块内存空间的时候,这个对象的引用计数就是1,当有对象要引用这块内存空间的时候,这个引用计数就增加1,当有对象不再引用这块内存的时候引用计数就减1,当这个引用计数减为0的时候就使用delete删除掉这块内存,这样就做到了当有对象引用的时候会正常的访问这块内存,引用完毕也可以正常的回收。但是如果每一个对象全部是手动retain和release,那么我们的代码里就会到处都是retain、release,既不美观方便也容易出错,于是我们引入了自动内存管理池对对象进行管理。看如下代码,引用计数问题就会比较清楚了。

//对象创建的时候引用计数被设置为1,这个是在它的构造函数中完成的,它会先调用父类CCOjbect的构造函数
//CCObject的构造函数如下所示
//CCObject::CCObject(void)
//,m_uReference(1) {}// when the object is created,the reference count of it is 1
CCSprite * sprite = new CCSprite();

CCLOG("retain count:%d",sprite->retainCount()); //值为1

//调用retain方法的时候引用计数增加1
sprite->retain();
CCLog("retain count:%d",sprite->retainCount());

//调用release方法的时候引用计数减一,当这个引用计数减为0的时候,在release方法中会delete掉这个对象
sprite->release();
CCLog("retain count:%d",sprite->retainCount());

//当我们调用autorelease方法的时候会调用这段代码
//CCPoolManager::sharedPoolManager()->addObject(this);
//调用autorelease方法的时候对象会被放到自动回收池中,这个自动回收池在每帧结束的时候会调用一次对象的release方法
sprite->autorelease();
CCLog("retain count:%d",sprite->retainCount());//记住 autoRelease并不会增加引用计数
上面代码有一处不太好理解就是经常说的自动回收机制,也就是autorelease方法,这个方法到底做了什么,有什么作用,我们需要好好的搞清楚!首先需要澄清的一个概念就是帧,在Cocos2d-x中我们经常说每秒多少多少帧,其实这个帧需要多少时间不是固定的,这个需要看每帧我们需要做多少事情,如果每一帧我们需要渲染很多的东西,那这一帧执行的时间当然就会很长的,游戏显得就会很卡,这个时候每秒的帧率就会下降的,所以不是时间决定的帧率,而是帧影响的时间!这个自动回收池就是在每帧结束的时候起作用的,在游戏的每一帧都会有一个大的循环,在一帧开始之前,系统建立了一个内存回收池,在这一帧的过程中,当我们调用了autorelease方法以后,我们的对象就会放到这个内存回收池中,当一帧结束的时候这个内存回收池就会释放掉,这个时候在内存回收池中的对象就会被release一下,也就是说引用计数就会减一,如果这个时候引用计数为0,就会删除对象了。如果引用计数不为0的话对象是不会被删除的,这时改由对象的持有者管理其生存周期了,下一帧开始的时候系统又会创建一个内存回收池,这个时候在上一次添加的对象这个时候是不会重新添加到这个内存回收池中的,在这个内存回收池中的对象是你在这一帧中调用了autorelease函数的对象。好了下面我们来看一个例子来了解下这个自动回收机制怎么就做到自动回收的。
<code cpp>
//在create的时候调用了sprite的autorelease方法
CCSprite * sprite = CCSprite::create("HelloWorld.png");
CCLog("retain count:%d",sprite->retainCount()); //retain 1
this->addChild(sprite);
CCLog("retain count:%d",sprite->retainCount()); //retain 2
分析一下上边的代码,调用了create工厂方法以后,内部的实现是先new一个CCSprite的对象,这个时候引用计数加1,然后调用autorelease方法,将这个对象放到了自动回收池中,因为这一帧还没有结束,当然引用计数就还是1,所以打印的结果就是1,当我们调用addChild的时候,传入这个CCSprite对象,这个时候在当前层接受了这个对象以后会把它的引用计数加一,表明当前层正在使用这块内存空间,所以现在的retain就是2了。当这一帧结束的时候自动回收池会将对象的引用计数-1,所以现在就只有CCLayer在引用这个对象了,当CCLayer析构的时候,它会调用这个对象的release方法,这个时候当然就会删除了这个CCSprite对象了。所以自动回收机制的自动就是在这一帧结束的时候将对象开始new的时候加的那个引用计数减掉,而让引擎中持有对象引用的其他对象去管理这个对象,当持有者removeChild()时或者自身析构的时候该对象就会被释放,即对象的持有者负责管理对象的retain和release,从而实现谁持有,谁管理,谁释放的原则。

*** cocos2d-x内存管理CCObject与autorelease深入分析
引用计数器是一种内存管理方式。通过一个无符号的成员变量计算当前有多少使用者在使用本内存。每次外部对象使用本内存时计数器加1,使用完要释放本内存时计数器减1,当计数器减为0时才真正进行占用内存释放。这样做可以实现在多个使用者使用一块内存时,只有当所有的使用者都确定不再使用这块内存的时候才进行内存的释放。避免了在还有使用者在使用内存时提前释放内存而导致的程序崩溃。

有兴趣的同学可以看引擎的源编,在源码中我们看到CCObject做的工作比较简单,它主要就是有两个功能。一个是通过引用计数交给内存管理器进行内存管理。另一个就是通过脚本ID访问相应的脚本。脚本的分析暂时不探讨,我们暂时只把内存管理的事情搞明白。我们来重点看一下autorelease函数的意义,顾名思义,“自动释放”。也就是说调用此函数则当前CCObject实例对象不需要用户在外部去手动调用release进行内存的释放工作。我们上面已经知道它通过引用计数来处理在什么时候内存释放,那么Cocos2d-x是怎么做到的? 在autorelease函数中有这么一句:CCPoolManager::sharedPoolManager()->addObject(this); CCPoolManager代表了内存管理器。此句调用CCPoolManager的实例对象的addObject函数将当前CCObject实例对象的指针交给内存管理器,内存管理器负责管理CCAutoReleasePool,CCAutoReleasePool中加入的就是被自动管理的对象。 下面来看一下内存管理器的原理: CCMutableArray是一个CCObject指针容器类,它内部通过使用STL的vector容器来存储CCObject指针。在加入一个新CCObject时对其引用计数器加1,在移除CCObject时对其引用计数器减1。有兴趣的同学可以自行打开CCMutableArray.h及cpp文件进行查看。 那么上面的介绍大家应该已经有了一个比较清楚的了解了, 最后再总结一下:Cocos2d-x提供了一个内存管理器类CCPoolManager,它有一个容器,而这个容器是用来存放了一些容器管理类CCAutoreleasePool的实例对象的。需要自动进行内存释放的CCObject实例对象会把其指针存放在容器管理类CCAutoreleasePool的实例对象中的m_pManagedObjectArray容器里。所有存在其中的CCObject实例对象在进行释放操作时通过使用计数器来进行判断在何时真正delete内存。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读