cocos2d开发的游戏-x retain和release倒底怎么玩

【Cocos2d-x v3.x官方文档】内存管理
招聘信息:
转自,托管于
Cocos2d-x的内存管理机制概述
在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的使用:
TestObject&*obj1&=&new&TestObject("testobj1");&CCLOG("obj1&referenceCount=%d",obj1->getReferenceCount());&obj1->retain();&CCLOG("obj1&referenceCount=%d",obj1->getReferenceCount());&obj1->release();&CCLOG("obj1&referenceCount=%d",obj1->getReferenceCount());&obj1->release();&
控制台显示的日志如下:
cocos2d:&TestObject:testobj1&is&created&cocos2d:&obj1&referenceCount=1&cocos2d:&obj1&referenceCount=2&cocos2d:&obj1&referenceCount=1&cocos2d:&TestObject:testobj1&is&destroyed&
通过例子和打印结果可以看到,obj1对象创建后,引用计数为1;执行一次retain()后,引用计数为2;执行一次release()后,引用计数回到1;再执行一次release()后,对象会被释放掉。
因此,我们可以调用retain()方法,令其引用计数增1,表示获取该对象的引用权;在引用结束的时候调用release()方法,令其引用计数值减1,表示释放该对象的引用权。直到对象的引用计数为0,释放该对象。
autorelease 使用
同样一段简单的例子来学习autorelease的使用,代码如下:
TestObject&*obj&=&new&TestObject("testobj");&CCLOG("obj&referenceCount=%d",obj->getReferenceCount());&obj->autorelease();&CCLOG("obj&is&add&in&currentpool&%s",PoolManager::getInstance()->getCurrentPool()->contains(obj)?"true":"false");&CCLOG("obj&referenceCount=%d",obj->getReferenceCount());&obj->retain();&CCLOG("obj&referenceCount=%d",obj->getReferenceCount());&obj->release();&CCLOG("obj&referenceCount=%d",obj->getReferenceCount());&&&Director::getInstance()->replaceScene(this);&
控制台显示的日志如下:
cocos2d:&TestObject:testobj&is&created&cocos2d:&obj&referenceCount=1&cocos2d:&obj&is&add&in&currentpool&true&cocos2d:&obj&referenceCount=1&cocos2d:&obj&referenceCount=2&cocos2d:&obj&referenceCount=1&...&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的使用,代码如下:
TestObject&*obj2&=&new&TestObject("testobj2");&CCLOG("obj2&referenceCount=%d",obj2->getReferenceCount());&&{&&&&AutoreleasePool&&&&&obj2->retain();&&&&CCLOG("obj2&referenceCount=%d",obj2->getReferenceCount());&&&&obj2->release();&&&&CCLOG("obj2&referenceCount=%d",obj2->getReferenceCount());&&&&obj2->autorelease();&&&&CCLOG("obj2&is&add&in&pool&%s",pool.contains(obj2)?"true":"false");&&&&TestObject&*obj3&=&new&TestObject("testobj3");&&&&obj3->autorelease();&&&&CCLOG("obj3&is&add&in&pool&%s",pool.contains(obj3)?"true":"false");&}&cocos2d:&TestObject:testobj2&is&created&cocos2d:&obj2&referenceCount=1&cocos2d:&obj2&referenceCount=2&cocos2d:&obj2&referenceCount=1&cocos2d:&obj2&is&add&in&pool&true&cocos2d:&TestObject:testobj3&is&created&cocos2d:&obj3&is&add&in&pool&true&cocos2d:&TestObject:testobj2&is&destroyed&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对象密集的区域(通常是循环中)的前后,我们最好可以手动创建并释放一个回收池。
&{&&&&&AutoreleasePool&pool2;&&&&&char&name[20];&&&&&for&(int&i&=&0;&i&<&100;&++i)&&&&&{&&&&&&&&&snprintf(name,&20,&"object%d",&i);&&&&&&&&&TestObject&*tmpObj&=&new&TestObject(name);&&&&&&&&&tmpObj->autorelease();&&&&&}&}&
autorelease()的实质是将对象加入自动释放池,对象的引用计数不会立刻减1,在自动释放池被回收时对象执行release();autorelease()并不是毫无代价的,其背后的释放池机制同样需要占用内存和CPU资源。过多的使用autorelease()会增加自动释放池的管理和释放池维护对象存取释放的支出。在内存和CPU资源本就不足的程序中使得系统资源更加紧张。此时就需要我们合理创建自动释放池管理对象autorelease。不用的对象推荐使用release()来释放对象引用,立即回收。
特殊内存管理
1. 工厂方法
在Cocos2d-x中,提供了大量的工厂方法创建对象。仔细看你会发现,这些对象都是自动释放的。下面以label的create方法为例,代码如下:
Label*&Label::create()&{&&&&&auto&ret&=&new&Label();&&&&&&if&(ret)&&&&&{&&&&&&&&&ret->autorelease();&&&&&}&&&&&&return&&}&
我们可以发现,创建了一个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对象密集的区域。
CocoaChina是全球最大的苹果开发中文社区,官方微信每日定时推送各种精彩的研发教程资源和工具,介绍app推广营销经验,最新企业招聘和外包信息,以及Cocos2d引擎、Cocostudio开发工具包的最新动态及培训信息。关注微信可以第一时间了解最新产品和服务动态,微信在手,天下我有!
请搜索微信号&CocoaChina&关注我们!
微信扫一扫
订阅每日移动开发及APP推广热点资讯公众号:CocoaChina
您还没有登录!请或
点击量3431点击量3370点击量2753点击量2667点击量2080点击量1736点击量1648点击量1632点击量1590
&2018 Chukong Technologies,Inc.
京公网安备89因为功能和接口和objective-c版本的差不多,所以在内存管理上也采用objective-c引用计数的机制来实现内存管理。仔细看了一下cocos2d-x的源代码,确实写的很好,代码组织得很工整。它们所有类都是继承自CCObject, CCObject有retain(), release()和autorelease()等方法,和objective-c上的NSObject用法一致。每当CCObject对象初始化时它的引用计数reference-count为1, 调用retain()方法reference-count加1, 调用release()方法reference-count减1,当reference-count为0时释放该对象内存。autorelease()方法的作用是对象放进CCAutoreleasePool中进行管理,每当一次绘图结束后,CCPoolManager会对当前的内存池每个对象调用release()方法,一些reference-count为0的对象就会被内存释放。
每当通过new, copy方法获取一个对象时,都有义务在不用它的时候调用release()方法,如果我们是从其他方法中(例如静态方法)就不要调用release()方法,除非之前调用了retain()方法来表示要拥有这个对象一段时间。自定义每个类里的成员变量如果是继承自CCObject,在赋新值之前要先对新值retain(), 然后对旧值release(),这两个步骤不能省去或者调转。定义方法要返回一个对象时,如果该对象是在方法里通过new, copy来新建的,在返回该对象之前要调用autorelease()方法,例如很多类的静态方法返回它的实例时,都已经调用了autorelease()方法。
只要严格遵守以上规范,内存泄漏的问题应该是可以避免的,以后写c++程序时候,即使没有使用cocos2d-x, 都可以考虑引入通过引
用计数来管理内存这一套机制,既简单又有效。
【木头Cocos2d-x 037】retain和release倒底怎么玩?
Cocos2d-x js 内存管理
cocos2d-x之retain与release
没有更多推荐了,Cocos2d-x内存管理的一种实现
使用 Cocos2d-x 编写游戏,常被人吐槽,吐槽什么,当然是内存管理,C++ 的使用本以不易,而在 Cocos2d-x 添加了半内存自动管理,在这么一种 复合机制 下,使得出现内存问题的概率直线飙升~~ 而
使用 Cocos2d-x 编写游戏,常被人吐槽,吐槽什么,当然是内存管理,C++ 的使用本以不易,而在 Cocos2d-x 添加了半内存自动管理,在这么一种 复合机制 下,使得出现内存问题的概率直线飙升~~
而关于这一点,可能在于并没有一个通用的合理的内存管理方式,能够更好的使用 Cocos2d-x ,或者说,缺少那么一种 规范,如果存在了这么一种 规范,而使得 Cocos2d-x 更为简单优雅,那势必是游戏的开发过程,更加从新所欲,把重点放在游戏本身的设计之上。
Retain 与 Release 乃万恶之源
稍微了解一点就能知道 Cocos2d-x 是基于引用计数来管理内存的,应用计数的加减就是 retain 和 release 方法的实现。在大多数情况下我们不用 显示 的去调用这两种方法,如在 CCNode 的 addChild 和 removeChild 方法,CCArray 的 addObject 和 removeObject 等这样成双成对的方法,对于这些的使用很简单,一叶上篇文章 Cocos2d-x 内存管理浅说 从概念上简单的分析了内部对象的生命周期特点,在此 浅说 之中,我刻意的绕过了它的底层实现,并没有深究其原理,对引用计数等概念也只是几句话一带而过,重点放在使用者该关心什么,该注意什么。因为我觉得 引用计数是个坑,一个很大的坑 ~
当我们想要长期 持有 某个对象的时候,我们会用到 retain 和 release 方法,而这种情况我们会经常遇到,如那些 非CCNode 类型,比如一个运行场景里面有一个 CCString (以CCString 为例,显然此刻你更愿意用 std::string)保存的场景名称,以便我们随时使用,那我们一个简单的做法就是在场景初始化的时候创建 CCString 对象,赋值,然后 retain,在场景结束或者析构函数中 release,这很简单,一个 retain 对应一个 release 就没有问题了,如果问题稍微变的复杂,在程序的运行中,我们可能会改变这个属性值,创建一个新的 CCString 去替换它,那在执行这些操作的时候我们需要很多判断,是否已经有值,首先要解除之前的引用,在重新引用新的对象~~诸如此类,如果中间不需要此对象,中间直接释放,那么我们会 非常华丽的看到在程序代码之中到处穿插着 retain 和 release 操作。而这些 retain 和 release 虽然成对出现,但不一定在同一个方法,这就演变成了,所在的不同方法也要成对的调用。
你把青蛙放到冷水里,再慢慢地加热,青蛙感觉不到什么,直到最后被烫死。 使用 retain 和 release 就正如温水里的青蛙,刚开始到也没觉得什么,引用计数概念多好。而到后来,发现越来越难以控制,为时以晚矣~
&如果说C语言给了你足够的绳子吊死自己,那么C++给的绳子除了够你上吊之外,还够绑上你所有的邻居,并提供一艘帆船所需的绳索。&(摘自 UNIX痛恨者手册) 而此时 ~~~
建立规范 完全消灭 retain 和 release
既然说 retain 和 release 乃万恶之源,那么我们只要 从源头上,解决这个问题,如此一切将会变的非常简单,我们将建立一种类似 addChild 这样的 内部处理 机制,不用显示的调用 retain 和 release ,从而杜绝了 retain &漫天飞&的可能。而要实现这样的机制,只需简单的设计即可 ~代码实现如下源码示例
&&#define&LS_PRE(p)&__ls_##p&&&&&&&&&&&&&&&&#define&LS_PROPERTY_RETAIN(varType,&varName,&funName)\&&&&&CC_SYNTHESIZE_RETAIN(varType,&LS_PRE(varName),&funName);&&&&#define&LS_P_INIT(p)&LS_PRE(p)(0)&#define&LS_P_RELEASE(p)&CC_SAFE_RELEASE_NULL(LS_PRE(p))&&&&&&class&LUser:&public&cocos2d::CCObject{&public:&&&&&CREATE_FUNC(LUser);&&&&&virtual&bool&init(){&&&&&&&&&return&true;&&&&&};&&&&&LUser(){&&&&&&&&&CCLog(&LUser()&);&&&&&};&&&&&~LUser(){&&&&&&&&&CCLog(&LUser().~():%s&,&m_sUserName.c_str());&&&&&};&&&&&&&std::string&m_sUserN&&&&&&&&&&&&&std::string&m_sP&&&&&&&&&};&&&class&PropertyTest:&public&CCLayer{&public:&&&&&CREATE_FUNC(PropertyTest);&&&&&&&virtual&bool&init(){&&&&&&&&&CCLog(&PropertyTest().init()&);&&&&&&&&&LUser*&lu&=&LUser::create();&&&&&&&&&lu-&m_sUserName&=&&leafsoar&;&&&&&&&&&lu-&m_sPassword&=&&123456&;&&&&&&&&&setLUser(lu);&&&&&&&&&&&&&&&&&&&&this-&scheduleUpdate();&&&&&&&&&&&return&true;&&&&&};&&&&&&&virtual&void&update(float&fDelta){&&&&&&&&&&&&&&&&&&&&&&&&&&if&(updateCount&&&5){&&&&&&&&&&&&&&&&&updateCount&++;&&&&&&&&&&&&&&&&&CCLog(&update&index:&%d&,&updateCount);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&if&(updateCount&==&1){&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&if&(getLUser())&&&&&&&&&&&&&&&&&&&&&&&&&CCLog(&log&lu:&%s&,&getLUser()-&m_sUserName.c_str());&&&&&&&&&&&&&&&&&&&}&else&if&(updateCount&==&2){&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&LUser*&lu&=&LUser::create();&&&&&&&&&&&&&&&&&&&&&lu-&m_sUserName&=&&一叶&;&&&&&&&&&&&&&&&&&&&&&setLUser(lu);&&&&&&&&&&&&&&&&&}&else&if&(updateCount&==&3){&&&&&&&&&&&&&&&&&&&&&if&(getLUser())&&&&&&&&&&&&&&&&&&&&&&&&&CCLog(&log&lu:&%s&,&getLUser()-&m_sUserName.c_str());&&&&&&&&&&&&&&&&&}&else&if&(updateCount&==&4){&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&setLUser(0);&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&CCLog(&update&index:&%d&end&,&updateCount);&&&&&&&&&&&&&}&&&&&&&&&};&&&&&&&&&&&&PropertyTest():&&&&&&&&&LS_P_INIT(m_pLUser),&&&&&&&&&updateCount(0)&&&&&{&&&&&};&&&&&&&&&&&&~PropertyTest(){&&&&&&&&&LS_P_RELEASE(m_pLUser);&&&&&};&&&&&&&&&&&&&&&&&&&&&&LS_PROPERTY_RETAIN(LUser*,&m_pLUser,&LUser);&&&private:&&&&&int&updateC&};&&&&cocos2d-x&debug&info&[PropertyTest().init()]&&cocos2d-x&debug&info&[LUser()]&cocos2d-x&debug&info&[update&index:&1]&&cocos2d-x&debug&info&[log&lu:&leafsoar]&cocos2d-x&debug&info&[update&index:&1&end]&cocos2d-x&debug&info&[update&index:&2]&&cocos2d-x&debug&info&[LUser()]&&cocos2d-x&debug&info&[LUser().~():leafsoar]&cocos2d-x&debug&info&[update&index:&2&end]&cocos2d-x&debug&info&[update&index:&3]&&cocos2d-x&debug&info&[log&lu:&一叶]&cocos2d-x&debug&info&[update&index:&3&end]&cocos2d-x&debug&info&[update&index:&4]&&cocos2d-x&debug&info&[LUser().~():一叶]&cocos2d-x&debug&info&[update&index:&4&end]&cocos2d-x&debug&info&[update&index:&5]&cocos2d-x&debug&info&[update&index:&5&end]&
通过上面的例子,可以看到将 持有对象 的操作变的非常简单,只通过 set 和 get 属性包装器存取数据,而并没有 显示 的调用 retain 和 release 方法来操作,最大程度的自动化管理引用计数问题,一切皆在掌控之中。从此,世界清净了 ~ 你不用再为何时 retain 何处 release 而烦恼。
而要做到如上的使用方法,在定义之初需规范化设计,大致如下:
& & & & 1.通过 LS_PROPERTY_RETAIN 宏创建 可持有对象属性,并自动创建 set 和 get 属性包装器。宏的设计并非毫无来由,我们知道 cocos2d-x 内部定义了很多以 CC_ 为前缀的宏,方便使用,比如 CC_PROPERTY[xxx] 此类。set 方法会自动的根据需要处理 retain 和 release。&
& & & & 2.宿主类的构造函数必须初始化对象为 NULL 或者 0,这是 C++ 的特性使然。LS_P_INIT,简化了操作。&
& & & & 3.宿主类的析构函数必须释放对象[如果有],这样我们就不用 显示 的调用释放了。可以通过 LS_P_RELEASE 调用。&
LS_PROPERTY_RETAIN 宏的实现
在上面的例程中,我们使用了 LS_PROPERTY_RETAIN(LUser*, m_pLUser, LUser); 定义一个属性,那么我们看这个宏做了哪些事情,我们展开这个宏看看:
LS_PROPERTY_RETAIN(LUser*,&m_pLUser,&LUser);&&private:&&&&&&&&&&LUser*&__ls_m_pLU&public:&&&&&&&&&&void&setLUser(LUser*&var){&&&&&&&&&&&&&&&&&&if&(__ls_m_pLUser&!=&var){&&&&&&&&&&&&&&&&&&&&&&&&&&CC_SAFE_RETAIN(var);&&&&&&&&&&&&&&&&&&&&&&&&&&CC_SAFE_RELEASE(__ls_m_pLUser);&&&&&&&&&&&&&&&&&&&&&&&&&&__ls_m_pLUser&=&var;&&&&&&&&&}&&&&&};&public:&&&&&LUser*&&getLUser(){&&&&&&&&&&&&&&&&&&return&__ls_m_pLU&&&&&};&
基本在设计之时,满足以上规范,就能想这里一样,通过 set 和 get 简单的对可持有对象进行任意的操作了。
这样的设计使得 所有基于 CCObject 的类型都能够方便的使用。那我们就能够很容易的持有 CCNode、层、精灵以及CCArray,等数据了。而且不会看到漫天飞舞的 retain 和 release ~
当然作用还不止如此,我们可能创建自己的类型继承 CCObject 以方便统一管理,在配合 CCArray ,使自定义的数据和 cocos2d-x 无缝的集成。有些游戏需要处理很多数据,如网络传输接受的数据,自定义常用数据等 ~
文中我们自定义了 LUser 是继承于 CCObject 的,这只是简单数据类型,复杂点的,LUser 中包含了其它 CCObject 的数据,如果按照以前的写法,设置之后就 retain ,那很难判断在哪里 release。如下:
class&LUser:&public&cocos2d::CCObject{&public:&&&&&CREATE_FUNC(LUser);&&&&&virtual&bool&init(){&&&&&&&&&return&true;&&&&&};&&&&&LUser(){&&&&&&&&&CCLog(&LUser()&);&&&&&};&&&&&~LUser(){&&&&&&&&&CCLog(&LUser().~():%s&,&m_sUserName.c_str());&&&&&};&&&&&&&std::string&m_sUserN&&&&&&&&&&&&&std::string&m_sP&&&&&&&&&&&&&&&&&&&&CCSprite*&m_pS&};&&&&LUser*&lu&=&LUser::create();&lu-&m_sSprite&=&CCSprite::create(&a.png&);&&lu-&m_sSprite-&retain();&
LUser 持有 m_sSprite 正如 文中 PropertyTest 持有 m_pLUser 一样,我们重新设计:
class&LUser:&public&cocos2d::CCObject{&public:&&&&&CREATE_FUNC(LUser);&&&&&virtual&bool&init(){&&&&&&&&&return&true;&&&&&};&&&&&LUser():&&&&&&&&&LS_P_INIT(m_pSprite)&&&&&{&&&&&&&&&CCLog(&LUser()&);&&&&&};&&&&&~LUser(){&&&&&&&&&CCLog(&LUser().~():%s&,&m_sUserName.c_str());&&&&&&&&&LS_P_RELEASE(m_pSprite);&&&&&};&&&&&&&std::string&m_sUserN&&&&&&&&&&&&&std::string&m_sP&&&&&&&&&&&&&&&&&&&&LS_PROPERTY_RETAIN(CCSprite*,&m_pSprite,&Sprite);&&&};&&&&LUser*&lu&=&LUser::create();&lu-&m_sUserName&=&&一叶&;&&lu-&setSprite(CCSprite::create(&a.png&));&
这样便将 m_pSprite 控制权,完全交给了 LUser 来处理了。基于这样的考虑,我们完全可以使用复杂的自定义类型,包含很多 CCObject 属性,而属性之中可能又包含其它 CCObject 的类型,而并不用担心释放问题,谁持有,谁管理,谁释放(而不会出现 lu-&m_sSprite-&retain(); 这样的情况)。这些数据可以在游戏中任意的传递,并且都是CCObject 类型的,并很好的结合 CCArray 管理。让自定义类型与 cocos2d-x 两者天衣无缝,配合无间 ~
这里自定义的宏,加了个复杂的前缀,仅仅想提醒大家,只通过 set 和 get 来进行存取的操作,从而避免使用 retain 和 release 来管理,更简单的写法,使用 cocos2d-x 自带的宏即可:
&CC_SYNTHESIZE_RETAIN(LUser*,&__m_pLUser,&LUser);&&&&&&&CC_SAFE_RELEASE_NULL(__m_pLUser);&&&&
为什么 LUser 继承自 CCObject
如果不集成自 CCObject 而使用原来的 C++ 方式也并无不可,但 CCObject 的优势是很明显的,如果能够善于使用。如果你想在 cocos2d-x 一个CCNode绑定数据有 setUserObject() 方法,如果多个 LUser 那么可以用 CCArray 进行管理,如果你想使用通知功能 CCNotificationCenter,而此 LUser 是可被传递的,我们设置了 LUser 然后靠诉别人我更新了,发送一条通知,谁对这个通知感兴趣,那谁就自己处理去吧 ~ 如果 ~~~ 如果你对此文感兴趣,不妨一试 ~
CocoaChina是全球最大的苹果开发中文社区,官方微信每日定时推送各种精彩的研发教程资源和工具,介绍app推广营销经验,最新企业招聘和外包信息,以及Cocos2d引擎、Cocos Studio开发工具包的最新动态及培训信息。关注微信可以第一时间了解最新产品和服务动态,微信在手,天下我有!
请搜索微信号“CocoaChina”关注我们!
关注微信 每日推荐
扫一扫 浏览移动版cocos2d-x ccarray能不能做类成员,能的话怎么retain_百度知道
cocos2d-x ccarray能不能做类成员,能的话怎么retain
&#xe6b9;答题抽奖
首次认真答题后
即可获得3次抽奖机会,100%中奖。
采纳数:131
获赞数:582
可以,create之后retain一下,在类的析构函数里release一下,就可以了。
xpsuperman174
xpsuperman174
采纳数:554
获赞数:2690
可以的啊!CCArray的变量也没有什么了不起的,你只需要把这个变量new出来,不delete就可以了。这是C++写法,不是OC写法,没有alloc, retain和release。
本回答被网友采纳
采纳数:13
获赞数:49
可以, CCArray 只是一个容器,
可以存任意类型的值。操作很方便,直接new就ok
为你推荐:
其他类似问题
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。retain和倒底怎么玩?
呼呼,好久没有发布教程了(小若:难得清静了,你为毛又出来吓人= =),其实最近木头我在准备出版书籍的事情。但是貌似不太顺利,果然我还是积累不够,写书的过程压力好大,感觉写不出有趣的文字出来(小若:嗷、、、)。果然还是在博客写自由一些?嘿嘿~
最近以及最不是很近(小若:书里一定不能出现这些错误的语句,所以你才写不出来吧),不少朋友对的认识似乎有点模糊,今天我就和大家分享一下关于的知识吧
笨木头花心贡献,啥?花心?不呢,是用心~转载请注明,原文地址:
1. 为什么会有?
C++和不一样,有一套很方便的垃圾回收机制,当我们不需要使用某个对象时,给它赋予值即可。而了一个对象之后,不使用的时候通常需要掉。
于是,就发明了一套内存管理机制(小若:发你妹纸。。。),其实红孩儿的博客很详细地解释了的内存管理机制,我没有能力也不想重复解释。(小若:那你还写?)
Retain的意思是保持引用,也就是说,如果想保持某个对象的引用,避免它被释放,那就要调用对象的函数。(小若:为什么不就会被释放?)
2. 真正的凶手
既然旁白诚心诚意地问我,那我就光明正大地回答吧(小若:我今天没力气吐槽,好吧)。
一旦调用对象的函数,那么这个对象就被的内存管理机制给盯上了,如果这个对象没人认领,那就等着被释放吧。(小若:太久没吐槽,一时不知道吐什么好)。
3. 看代码实际点
说了这么多,还是上代码吧。
创建一个的项目,就直接拿开刀,修改函数,在最后添加一句代码:
bool HelloWorld::init()
bool bRet =
/* 很多代码被省略了。。。。。。 */
testSprite = CCSprite::create("HelloWorld.png");
} while (0);
(小若:是什么东东?)
testSprite是一个成员变量,在头文件里加上就可以了:
class HelloWorld : public cocos2d::CCLayer
virtual bool init();
static cocos2d::CCScene* scene();
void menuCloseCallback(CCObject* pSender);
CREATE_FUNC(HelloWorld);
cocos2d::CCSprite* testS
然后,最关键的来了,我们修改函数:
void HelloWorld::menuCloseCallback(CCObject* pSender)
testSprite-&getPosition();
现在,运行项目,点击按钮,看看是什么情况?
(小若:报错了!)
如果大家知道怎么调试项目的话,我们在函数里断点,用调试模式运行项目,看看对象:
(小若:很正常啊,有什么特别的?)
正你妹纸啊,正!你才正!(小若:不要这么光明正大地赞我!)
我们应该能看到不少非正常数据,图中已经用红色圈圈标出来了,这代表对象被释放了,现在指向未知的位置。
这是很危险的,有时候它不会立即报错,但是在某个时刻突然崩溃!
要想解决这个问题,很简单,再次修改函数:
bool HelloWorld::init()
bool bRet =
/* 很多代码被省略了。。。。。。 */
testSprite = CCSprite::create("HelloWorld.png");
  testSprite-&retain();
} while (0);
再次运行项目,看看还会不会报错?(小若:不会了,为什么?)
再次用调试模式运行项目,看看对象:
(小若:不正常!都是!!)
零你妹纸(小若:为什么今天你总是抢我的对白!)
这次我们看到的数据明显正常了。
4. 原理来了
好了,唠叨了一大堆,还没有进入正题。
首先,要想让对象参与内存管理机制,必须继承类(、等都继承了类)。
然后,调用对象的函数,对象就会被的内存管理机制盯上,在游戏的每一帧,内存管理机制都会扫描一遍被盯上的对象,一旦发现对象无人认领,就会将对象杀死!(小若:嗷残忍!)
如果不想让对象被杀死,那么就要调用对象的函数,这样对象就被认领了,一旦对象被认领,就永远不会被内存管理机制杀掉,是永远,一辈子。(小若:好朋友,一辈子)
但,对象一辈子都不被释放的话,那么就会产生内存泄露,你试试加载一个占内存的对象一辈子不释放,不折腾死才怪(小若:你去加载一个的对象本身就是闲的那个什么疼啊!)因此,当你不需要再使用这个对象时,就要调用对象的函数,这是和对应的。一般可以在析构函数里调用函数。
5. 实际情况
讲道理,大家都懂,但是,相信很多朋友在实际写代码的时候,还是会感觉很混乱。
比如,什么时候该?大家是不是发现,有时候不也不会报错?
其实这很简单,因为我们经常会在一个对象之后,添加到层里,如:
testSprite = CCSprite::create("HelloWorld.png");
this-&addChild(testSprite);
addChild函数就是导致大家混乱的凶手了,函数会调用对象的函数,为什么它要调用对象的函数呢?因为你都把对象送给它当孩子了,它当然要认领这个对象了!(小若:我懂了,嗷!)
于是,当我们把对象到时(不一定是,、都行),我们就不需要调用对象的函数了。
6. 那倒底什么时候要?
说了这么多,还是没有说清楚,什么时候要调用对象的。
很简单,当你把一个对象作为成员变量时,并且没有把对象到另外一个对象时,就需要调用函数。
7. 最后的最后
一定要记住,必须要调用了对象的函数之后,和函数才会生效,否则,一切都是徒劳。
因此,十分建议使用的方式创建对象,如:
CCSprite* CCSprite::create(const char *pszFileName)
CCSprite *pobSprite = new CCSprite();
if (pobSprite && pobSprite-&initWithFile(pszFileName))
pobSprite-&autorelease();
return pobS
CC_SAFE_DELETE(pobSprite);
return NULL;
这些就是retain表面上的知识了,至于retain源码级别的解说,请到红孩儿的博客吧,强烈推荐~
好了,不唠叨了困喇,睡大觉去
cocos2dx学习笔记之retain()和release()函数简介
cocos2dx的retain与release
Cocos2d-x 2.0.4 小心隐藏的retain
cocos2d-x之retain与release
cocos2d-x之retain和release
retain和release倒底怎么玩?
cocos retain and release
cocos2d-x和objective-c中的retain()和release()
java 集合 差集 交集 合集
没有更多推荐了,

我要回帖

更多关于 白鹭引擎还是cocos2d 的文章

 

随机推荐