热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

Cocos2d-x的内存管理机制概述-mysql教程

在3.0版本,Cocos2d-x采用全新的根类Ref,实现Cocos2d-x类对象的引用计数记录。引擎中的所有类都派生自Ref。基本类图:Cocos2d-x中所有继承自Ref的类,都可以使用Cocos2d-x的内存管理。Cocos2d-x提供引用计数管理内存。调用retain()方法,令其引用计数

在3.0版本,Cocos2d-x采用全新的根类Ref,实现Cocos2d-x 类对象的引用计数记录。引擎中的所有类都派生自Ref。 基本类图: Cocos2d-x中所有继承自Ref的类,都可以使用Cocos2d-x的内存管理。 Cocos2d-x 提供引用计数管理内存。调用retain()方法,令其引用计数

在3.0版本,Cocos2d-x采用全新的根类Ref,实现Cocos2d-x 类对象的引用计数记录。引擎中的所有类都派生自Ref。 基本类图:

Cocos2d-x中所有继承自Ref的类,都可以使用Cocos2d-x的内存管理。

Cocos2d-x 提供引用计数管理内存。调用retain()方法,令其引用计数增1,表示获取该对象的引用权;在引用结束的时候调用release()方法,令其引用计数值减1,表示释放该对象的引用权。

通过调用autorelease()方法,将对象放入自动释放池。当释放池自身被释放的时候,它就会对池中的所有对象执行一次release()方法,实现灵活的垃圾回收。

Cocos2d-x 提供AutoreleasePool,管理自动释放对象。当释放池自身被释放的时候,它就会对池中的所有对象执行一次release()方法。

retain 和 release 使用

下面一段简单的例子来学习 retain 和 release的使用:

  1. TestObject *obj1 = new TestObject("testobj1");
  2. CCLOG("obj1 referenceCount=%d",obj1->getReferenceCount());
  3. obj1->retain();
  4. CCLOG("obj1 referenceCount=%d",obj1->getReferenceCount());
  5. obj1->release();
  6. CCLOG("obj1 referenceCount=%d",obj1->getReferenceCount());
  7. obj1->release();

控制台显示的日志如下:

  1. cocos2d: TestObject:testobj1 is created
  2. cocos2d: obj1 referenceCount=1
  3. cocos2d: obj1 referenceCount=2
  4. cocos2d: obj1 referenceCount=1
  5. cocos2d: TestObject:testobj1 is destroyed

通过例子和打印结果可以看到,obj1对象创建后,引用计数为1;执行一次retain()后,引用计数为2;执行一次release()后,引用计数回到1;再执行一次release()后,对象会被释放掉。

因此,我们可以调用retain()方法,令其引用计数增1,表示获取该对象的引用权;在引用结束的时候调用release()方法,令其引用计数值减1,表示释放该对象的引用权。直到对象的引用计数为0,释放该对象。

autorelease 使用

同样一段简单的例子来学习autorelease的使用,代码如下:

  1. TestObject *obj = new TestObject("testobj");
  2. CCLOG("obj referenceCount=%d",obj->getReferenceCount());
  3. obj->autorelease();
  4. CCLOG("obj is add in currentpool %s",PoolManager::getInstance()->getCurrentPool()->contains(obj)?"true":"false");
  5. CCLOG("obj referenceCount=%d",obj->getReferenceCount());
  6. obj->retain();
  7. CCLOG("obj referenceCount=%d",obj->getReferenceCount());
  8. obj->release();
  9. CCLOG("obj referenceCount=%d",obj->getReferenceCount());
  10. //obj in current pool will be release
  11. Director::getInstance()->replaceScene(this);

控制台显示的日志如下:

  1. cocos2d: TestObject:testobj is created
  2. cocos2d: obj referenceCount=1
  3. cocos2d: obj is add in currentpool true
  4. cocos2d: obj referenceCount=1
  5. cocos2d: obj referenceCount=2
  6. cocos2d: obj referenceCount=1
  7. ...
  8. cocos2d: TestObject:testobj is destroyed

通过代码和打印结果,我们可以看到,obj对象创建后,引用计数为1;执行一次autorelease()后,obj对象被加入到当前的自动释放池。obj对象的引用计数值并没有减1。但是在下一帧开始前,当前的自动释放池会被回收掉,并对自动释放池中的所有对象执行一次release()操作,当对象的引用计数为0时,对象会被释放掉。

obj对象执行autorelease()后,我们对其执行了一组retain()和release()操作。此时obj对象的引用计数为1,在场景切换后,当前的自动释放池被回收,obj对象执行一次release()操作引用计数减为0时,对象会被释放掉。

注意:autorelease()只有在自动释放池被释放时才会进行一次释放操作,如果对象释放的次数超过了应有的次数,则这个错误在调用autorelease()时并不会被发现,只有当自动释放池被释放时(通常也就是游戏的每一帧结束时),游戏才会崩溃。在这种情况下,定位错误就变得十分困难了。例如,在游戏中,一个对象含有1个引用计数,但是却被调用了两次autorelease()。在第二次调用autorelease()时,游戏会继续执行这一帧,结束游戏时才会崩溃,很难及时找到出错的地点。

因此,我们建议在开发过程中应该避免滥用autorelease(),只在工厂方法等不得不用的情况下使用,尽量以release()来释放对象引用。

AutoreleasePool 使用

Cocos2d-x提供AutoreleasePool,管理自动释放对象。

下面一段简单的例子讲解AutoreleasePool的使用,代码如下:

  1. TestObject *obj2 = new TestObject("testobj2");
  2. CCLOG("obj2 referenceCount=%d",obj2->getReferenceCount());
  3. //use AutoreleasePool
  4. {
  5. AutoreleasePool pool;
  6. obj2->retain();
  7. CCLOG("obj2 referenceCount=%d",obj2->getReferenceCount());
  8. obj2->release();
  9. CCLOG("obj2 referenceCount=%d",obj2->getReferenceCount());
  10. obj2->autorelease();
  11. CCLOG("obj2 is add in pool %s",pool.contains(obj2)?"true":"false");
  12. TestObject *obj3 = new TestObject("testobj3");
  13. obj3->autorelease();
  14. CCLOG("obj3 is add in pool %s",pool.contains(obj3)?"true":"false");
  15. }
  1. cocos2d: TestObject:testobj2 is created
  2. cocos2d: obj2 referenceCount=1
  3. cocos2d: obj2 referenceCount=2
  4. cocos2d: obj2 referenceCount=1
  5. cocos2d: obj2 is add in pool true
  6. cocos2d: TestObject:testobj3 is created
  7. cocos2d: obj3 is add in pool true
  8. cocos2d: TestObject:testobj2 is destroyed
  9. cocos2d: TestObject:testobj3 is destroyed

通过代码和输出结果,可以看到,创建了一个obj2对象,此时obj2对象的引用计数为1。接着创建了一个自动释放池,对obj2对象执行retain()和release()操作后,执行autorelease()操作,此时obj2对象被加入到当前新建的自动释放池中。接着新建了obj3对象,并执行autorelease()操作。同样obj3也被加入到当前新建的自动释放池中。在代码块结束后,自动释放池被回收,加入自动释放池中的对象obj2和obj3执行release()操作,引用计数减为0,被释放销毁。

我们可以自己创建AutoreleasePool,管理对象的autorelease。

我们已经知道,调用了autorelease()方法的对象(下面简称”autorelease对象”),将会在自动释放池释放的时候被释放一次。虽然,Cocos2d-x已经保证每一帧结束后释放一次释放池,并在下一帧开始前创建一个新的释放池,但是我们也应该考虑到释放池本身维护着一个将要执行释放操作的对象列表,如果在一帧之内生成了大量的autorelease对象,将会导致释放池性能下降。因此,在生成autorelease对象密集的区域(通常是循环中)的前后,我们最好可以手动创建并释放一个回收池。

例如:

  1. // example of using temple autorelease pool
  2. {
  3. AutoreleasePool pool2;
  4. char name[20];
  5. for (int i = 0; i <100; &#43;&#43;i)
  6. {
  7. snprintf(name, 20, "object%d", i);
  8. TestObject *tmpObj = new TestObject(name);
  9. tmpObj->autorelease();
  10. }
  11. }

autorelease()的实质是将对象加入自动释放池,对象的引用计数不会立刻减1,在自动释放池被回收时对象执行release();autorelease()并不是毫无代价的,其背后的释放池机制同样需要占用内存和CPU资源。过多的使用autorelease()会增加自动释放池的管理和释放池维护对象存取释放的支出。在内存和CPU资源本就不足的程序中使得系统资源更加紧张。此时就需要我们合理创建自动释放池管理对象autorelease。不用的对象推荐使用release()来释放对象引用,立即回收。

特殊内存管理

1. 工厂方法

在Cocos2d-x中,提供了大量的工厂方法创建对象。仔细看你会发现,这些对象都是自动释放的。下面以label的create方法为例,代码如下:

  1. Label* Label::create()
  2. {
  3. auto ret = new Label();
  4. if (ret)
  5. {
  6. ret->autorelease();
  7. }
  8. return ret;
  9. }

我们可以发现,创建了一个Label的对象,并对该对象执行autorelease()。表示该对象是自动释放的。细心的你会发放Layer/Scene/Sprite等类的create方法都相同。

使用工厂方法创建对象时,虽然引用计数也为1,但是由于对象已经被放入了释放池,因此调用者没有对该对象的引用权,除非我们人为地调用了retain()来获取引用权,否则,不需要主动释放对象。

2. Node的addChild/removeChild方法

在Cocos2d-x中,所有继承自Node类,在调用addChild方法添加子节点时,自动调用了retain。

对应的通过removeChild,移除子节点时,自动调用了release。

调用addChild方法添加子节点,节点对象执行retain。子节点被加入到节点容器中,父节点销毁时,会销毁节点容器释放子节点。对子节点执行release。如果想提前移除子节点我们可以调用removeChild。

在Cocos2d-x内存管理中,大部分情况下我们通过调用addChild/removeChild的方式自动完成了retain,release调用。不需再调用retain,release。

总结

使用cocos2d-x的内存管理,要清楚当前对象的引用计数。 使用者对对象进行retain和release成对使用。 正确使用autorelease方法。灵活使用自动释放池管理一些autorelease对象密集的区域。

推荐阅读
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 本文介绍如何在 Unity 的 XML 配置文件中,将参数传递给自定义生命周期管理器的构造函数。我们将详细探讨 CustomLifetimeManager 类的实现及其配置方法。 ... [详细]
  • 本文探讨了在Linux系统上使用Docker时,通过volume将主机上的HTML5文件挂载到容器内部指定目录时遇到的403错误,并提供了解决方案和详细的操作步骤。 ... [详细]
  • 探讨如何真正掌握Java EE,包括所需技能、工具和实践经验。资深软件教学总监李刚分享了对毕业生简历中常见问题的看法,并提供了详尽的标准。 ... [详细]
  • 作为一名专业的Web前端工程师,掌握HTML和CSS的命名规范是至关重要的。良好的命名习惯不仅有助于提高代码的可读性和维护性,还能促进团队协作。本文将详细介绍Web前端开发中常用的HTML和CSS命名规范,并提供实用的建议。 ... [详细]
  • 本文探讨了在 ASP.NET MVC 5 中实现松耦合组件的方法。通过分离关注点,应用程序的各个组件可以更加独立且易于维护和测试。文中详细介绍了依赖项注入(DI)及其在实现松耦合中的作用。 ... [详细]
  • Startup 类配置服务和应用的请求管道。Startup类ASP.NETCore应用使用 Startup 类,按照约定命名为 Startup。 Startup 类:可选择性地包括 ... [详细]
  • 网易严选Java开发面试:MySQL索引深度解析
    本文详细记录了网易严选Java开发岗位的面试经验,特别针对MySQL索引相关的技术问题进行了深入探讨。通过本文,读者可以了解面试官常问的索引问题及其背后的原理。 ... [详细]
  • 自己用过的一些比较有用的css3新属性【HTML】
    web前端|html教程自己用过的一些比较用的css3新属性web前端-html教程css3刚推出不久,虽然大多数的css3属性在很多流行的浏览器中不支持,但我个人觉得还是要尽量开 ... [详细]
  • 本文将深入探讨如何在不依赖第三方库的情况下,使用 React 处理表单输入和验证。我们将介绍一种高效且灵活的方法,涵盖表单提交、输入验证及错误处理等关键功能。 ... [详细]
  • 本文探讨了如何在日常工作中通过优化效率和深入研究核心技术,将技术和知识转化为实际收益。文章结合个人经验,分享了提高工作效率、掌握高价值技能以及选择合适工作环境的方法,帮助读者更好地实现技术变现。 ... [详细]
  • 探索电路与系统的起源与发展
    本文回顾了电路与系统的发展历程,从电的早期发现到现代电子器件的应用。文章不仅涵盖了基础理论和关键发明,还探讨了这一学科对计算机、人工智能及物联网等领域的深远影响。 ... [详细]
  • 科研单位信息系统中的DevOps实践与优化
    本文探讨了某科研单位通过引入云原生平台实现DevOps开发和运维一体化,显著提升了项目交付效率和产品质量。详细介绍了如何在实际项目中应用DevOps理念,解决了传统开发模式下的诸多痛点。 ... [详细]
  • 本文详细介绍了 Flink 和 YARN 的交互机制。YARN 是 Hadoop 生态系统中的资源管理组件,类似于 Spark on YARN 的配置方式。我们将基于官方文档,深入探讨如何在 YARN 上部署和运行 Flink 任务。 ... [详细]
  • 本文详细探讨了如何在Docker环境中实现单机部署Redis集群的方法,提供了详细的步骤和配置示例,帮助读者更好地理解和应用这一技术。 ... [详细]
author-avatar
沉白
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有