`
7090
  • 浏览: 273345 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

(转)cocos2d内存管理

 
阅读更多
现在我们需要讨论一下内存管理和自动释放内存消息.

通常, 在objective-C中创建对象的时候,你会调用alloc方法. 这么做之后, 你就有责任在你不再需要它的时候释放它所占用的内存. 下面是典型的alloc/init 和release周期.
// allocate a new instance of NSObject
NSObject* myObject = [[NSObject alloc] init];

// do something with myObject here …

// release the memory used by myObject
// if you don’t release it, the object is leaked and the memory used by it is never freed.
[myObject release];

现在,有了autorelease消息和iOS应用总是使用的自动释放池, 就不再需要发送release消息了.
下面是用autorelease重写的相同的例子.
// allocate a new instance of NSObject
NSObject* myObject = [[[NSObject alloc] init] autorelease];
// do something with myObject here ...
// no need to call release, in fact you should not send release as it would crash.

可以看到, 这样简化了内存管理工作, 你不再需要惦记着发送release消息了.自动释放池会在随后帮你向对象发送release消息. 因为需要添加autorelease消息,创建对象的过程变得稍微复杂了一点.

考虑下面的代码, 它说明了按照传统的内存释放周期如何为CCNode对象分配内存.
// allocate a new instance of CCNode
CCNode* myNode = [[CCNode alloc] init]];
// do something with myNode ...
[myNode release];

不建议用这种方式创建cocos2d对象.使用静态的初始化器方法来返回自动释放内存对象,更加方便.和苹果推荐的方式不同,通过把[[[NSObject alloc] init] autorelease]调用移动到类自身的静态方法中, autorelease的使用被内置在了cocos2d的设计中. 这是很好的做法,不要抵触它.这会让你更加轻松, 因为你不再需要记住那个对象需要释放.手工释放内存, 常常会要么由于过度释放而导致程序崩溃, 要么由于忘记释放内存而导致内存泄漏.

在CCNode类中,静态初始化器是+(id) node, 下面的代码发送一个alloc消息给self, 等同于在CCNode类的实现中的[CCNode alloc].
+(id) node
{
return [[[self alloc] init] autorelease];
}

这种情况下通常使用self, 这是合法的. 这对于C++程序员来说可能不太容易理解.

看下面的代码,我们使用静态初始化器重写CCNode对象的分配.很明显, 代码变得短小,简明和整洁了. 这就是我喜欢的方式.
// allocate a new instance of CCNode
CCNode* myNode = [CCNode node];
// do something with myNode .

这就是使用自动释放内存的对象的优雅之处.你不必惦记着给它们发送release消息.每次cocos2d进入到下一帧, 不再被使用的自动释放内存对象会被自动释放掉.但是有一点需要注意,如果使用这段代码,只要后面有一个帧企图访问myNode对象, 这时候他已经被释放了,发送任何消息给它将会导致一个EXC_BAD_ACCESS异常.

仅仅把CCNode* myNode变量添加到类的成员变量中,并不意味着这个对象所使用的内存会被自动地保留. 如果你希望在后面的帧中保留自动释放内存对象,你必须保留对这个对象的引用,然后相应地释放它, 除非显式地加入到类的子节点中.

下面介绍一种比较好的方式,不需要显示调用retain方法也可以保持自动释放内存对象.通常创建了CCNode对象后,作为子节点添加到另一个CCNode子类的对象,这样就可以把这些CCNode对象加入到场景层次中.如果你愿意甚至可以删除这些成员变量,依赖cocos2d来为你存储对象.
// creating an autorelease instance of CCNode
-(void) init
{
myNode = [CCNode node];
myNode.tag = 123;

// adding the node as children to self (assuming self is derived from CCNode)
[self addChild:myNode];
}

-(void) update:(ccTime)delta
{
// later access and use the myNode object again
CCNode* myNode = [self getChildByTag:123];

// do something with myNode
}

内幕在于, addChild把对象添加到容器中, 例子中使用的是CCArray, 和iPhone SDK提供的NSMutableArray相似, 但是更快. CCArray,NSMutableArray和其它iPhone SDK的容器自动发送一个retain消息给加入其中的所有对象, 当它们从容器中移除时再给它们发送一条release消息.这样对象可以一直保持有效以便随后的访问, 同时,当它们从容器中移除时也可以被自动释放掉.

需要牢记的是,我在这里讨论的关于cocos2d的管理内存的方法是最好的. 你可能会遇到其他开发人员说自动释放内存不好或者缓慢而不应该使用.别听他们的.

提示:苹果开发者文档建议减少使用自动释放内存的对象的数量.然而,大多数cocos2d对象,都是作为自动释放内存对象创建的.这让内存管理更加容易,就像上面展示的那样.

如果你在每一个cocos2d对象上使用alloc/init和release方法, 你会陷入到很多毫无益处的痛苦中.也并不是说永远不要使用alloc/init, 它有它的作用,有时候甚至是必需的.但是对于cocos2d对象, 你应该使用静态自动释放内存初始化器.

自动释放内存对象只有一点需要注意.它们的内存会一直被占用, 直到游戏进入到下一帧.这就意味着如果你在每一帧中创建了大量舍弃式自动释放内存的对象, 会非常浪费内存.但是这实际上很少发生.

以上是我对cocos2d内存管理的快速入门.在Objective-C的世界,内存管理遵循两条简单的规则:
■ 如果你拥有(alloc, copy或者retain)一个对象, 你必须在随后释放它.
■ 如果你发送了autorelease消息给一个对象, 你不必释放它.

更深入的关于内存管理的讨论,请参阅苹果的内存管理编程指南(Apple’’s Memory Management Programming Guide):
(http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/Memor yMgmt/MemoryMgmt.html).

进化
像helloWorld这样的模板项目,如果不进行一些加工又有什么作用呢? 下面我会教你对触摸事件作出响应. 如何开始呢?

首先,在init方法中作两处改变,允许触摸输入和使用tag值以便随后通过此值取得label对象.修改的代码在列表2-3中以高亮显示.

列表 2–3. 允许触摸输入和使用tag值
-(id) init
{
if ((self = [super init])) {
// create and initialize a label
CCLabel* label = [CCLabel labelWithString:@"Hello World"
fontName:@"Marker Felt" fontSize:64];

// get the window (screen) size from CCDirector
CGSize size = [[CCDirector sharedDirector] winSize];

// position the label at the center of the screen
label.position = CGPointMake(size.width / 2, size.height / 2);

// add the label as a child to this Layer
[self addChild: label];

// our label needs a tag so we can find it later on
// you can pick any arbitrary number
label.tag = 13;

// must be enabled if you want to receive touch events!
self.isTouchEnabled = YES;
}
return self;
}

label对象的tag属性值被赋予13. 为什么这么做呢? 让你这么做一定是有原因的,对吧? 在前面一节中,我解释过怎样访问类的子节点对象—通过tag属性值找到它.tag属性的值可以是任意的正整数,而且每一个对象必须有它自己的唯一的tag数值, 否则你就不能标识你想要的的对象.

贴士:不要使用像13这样的魔法数字作为tag的值,应该养成定义常量来表示这些tag的值.相对于使用像kTagForLabel这样的定义良好的常量,以后你将很难想起tag值13代表的是怎样的对象. 我将会在第5章讨论这个问题.

其次,self.isTouchEnabled被设置成YES, 这是CCLayer的属性,并且告诉它你想要接收触摸消息.这时候ccTouchesBegan方法会被调用:
-(void) ccTouchesBegan:(NSSet*)touches withEvent:(UIEvent*)event;
{
CCLabel* label = (CCLabel*)[self getChildByTag:13];
label.scale = CCRANDOM_0_1();
}

使用[self getChildByTag:13], 可以得到CCLabel对象, 它的tag值在init方法中被设定.你可以和平常一样使用这个label. 在上面的代码中,我们使用cocos2d中非常方便的宏CCRANDOM_0_1() 让这个文本标签的大小在0和1之间变化. 每次你触摸屏幕,标签的大小都会改变.

因为getChildByTag方法总是返回label对象, 所以我们可以安全地把它转换成(CCLabel*)对象.但是需要注意的是,如果某种原因导致得到的对象不是从CCLabel类继承的对象,你的应用将崩溃.这很容易发生在你不小心将另一个对象的tag值也同样设置成了13. 因为这个原因,采用防御性的编程风格,确认一下你所得到的数据确实是你预期的数据,是一个很好的做法.防御性编程使用断言来确认假设是正确的.现在,你可以使用NSAssert方法:
-(void) ccTouchesBegan:(NSSet*)touches withEvent:(UIEvent*)event;
{
CCNode* node = [self getChildByTag:13];

// defensive programming: verify the returned node is a CCLabel class object
NSAssert([node isKindOfClass: [CCLabel class]], @"node is not a CCLabel!");

CCLabel* label = (CCLabel*)node;
label.scale = CCRANDOM_0_1();

}

本例中,我们期待的 getChildByTag 方法返回的节点对象是CCLabel类型的对象.但是我们并不确定,于是我们加了NSAssert来确认这个事实.这对于在程序崩溃前发现错误是很有帮助的.

这里增加了2行代码, 但是从性能的角度来看是一样的.NSAssert的调用在发布(Release)版的构建中会被完全消除.类型转换操作CCLabel* label = (CCLabel*)node; 也是做过的,只是在同一行中.总之, 两个版本的执行结果完全相同, 但是在第二个版本中,如果你没有得到期望的CCLabel对象,你会得到有用的提示信息, 而不是EXC_BAD_ACCESS崩溃信息.

还需要了解什么
因为这是入门的章节,我认为有必要向你介绍一些关于iOS的重要的但是经常被忽视的方面.我希望你了解各种iOS设备之间一些微妙的差异.特别是可用内存的大小常常被误解.因为你只能安全地使用每个设备的内存的一部分.

我也希望让你知道iPhone模拟器是测试你的游戏的一个非常好的工具, 但是它无法用来测量性能,内存的使用,以及其它一些特性.模拟器的体验和运行在实际的iOS设备上是有很大的不同的.不要落入基于iPhone 模拟器上游戏运行的效果来对游戏进行评价的陷阱.这只能依赖于实际的设备.

iOS设备
当你为各种iOS设备开发应用时,你需要考虑到他们之间的差异.大多数独立的游戏开发爱好者不会购买每个有着细微差异的iOS设备.但是至少我们需要理解他们之间的一些重要的差异.

你可以参阅苹果的规格表来熟悉iOS设备的技术规格.下面的链接分别列出了iPhone, iPod Touch和iPad的规格参数.

http://support.apple.com/specs/#iphone
http://support.apple.com/specs/#ipodtouch
http://support.apple.com/specs/#ipad

表2-1总结了和游戏开发相关的重要的硬件差异.表中列出的iPod Touch设备用”代” 作区分,是因为苹果不使用”3G”的后缀来命名iPod Touch的系列产品.这张表向你展示了iOS 设备之间不像你想象的那么同质.注意iPhone 4的显卡芯片是未知的,因为它首次集成在CPU中.

表2-1 iOS硬件差异

Device Processor Graphics Resolution Memory (RAM)
First generation devices 412 MHz PowerVR MBX 480x320 128 MB
iPhone 3G (second generation.) 412 MHz PowerVR MBX 480x320 128 MB
iPod Touch (second generation) 532 MHz PowerVR MBX 480x320 128 MB
Third generation devices 600 MHz PowerVR SGX535 480x320 256 MB
iPad 1000 MHz PowerVR SGX535 1024x768 256 MB
IPhone 4 1000 Mhz Unknown 960x640 512 MB


可以看到,每个更新一代的iOS设备通常都会有更快的CPU,更强的图形芯片,更大的内存和更高的分辨率.这个趋势还将继续,新的设备会变得越来越强大.如果你打算通过iOS游戏赚钱的话,记住旧型号的设备仍然有相当大的市场份额,这种市场份额的变化非常的慢,比新型号设备发布的速度要慢很多.甚至在今天,如果不让你的游戏支持第二代的设备,那么就等于放弃了相当大的一块市场.

通常,当游戏开发者研究硬件特性的时候,他们倾向于关注CPU的速度和图形芯片来评估可以采用的技术.然而,作为一种移动设备的iOS设备,直到最近的iPhone4, 仍然更多地受限于可用内存的大小.

提示:不要把内存(RAM)和闪存(flash storage memory)搞混淆了.闪存用来存储MP3,视频,应用程序和图像等文件,iOS设备最小的也有8G的闪存. 闪存相当于桌面电脑的硬盘.内存是应用程序在运行时用来存储代码,数据和材质的的地方.

转载请注明出处:http://blog.sina.com.cn/mountlook
分享到:
评论

相关推荐

    Cocos2D-X2.2.3学习笔记3(内存管理)

    Cocos2D-X2.2.3学习笔记3(内存管理) http://blog.csdn.net/hucblog/article/category/2242181

    Cocos2D-X游戏开发技术精解.pdf

    Cocos2D-X引擎的文件操作模块和内存管理机制;各种各样的粒子效果;如何掌握利用Lua脚本制作游戏的能力;Cocos2D-HTML5引擎版本;引擎的附加功能等。最后,《Cocos2D-X游戏开发技术精解》和读者一起展望了Cocos2D-X...

    Cocos2D-X游戏开发技术精解

    第1章 Cocos2D-X引擎的介绍 1 1.1 何为游戏引擎 1 1.1.1 游戏的核心—引擎 1 1.1.2 引擎的特点 2 1.1.3 知名的引擎介绍 4 1.1.4 引擎的分类 5 1.2 Cocos2D-X引擎的来历 8 1.3 引擎的版本 9 1.4 下载与安装 10 1.5 ...

    cocos2d-x游戏开发之旅

    介绍Cocos2d-x更高阶的内容,包括渲染效率的提高、动画、TexturePacker图片打包、Tiled地图游戏实例、定时器、函数回调、内存管理、数据保存、Csv文件读取。介绍Lua、有限状态机启蒙知识和应用。分享基于Cocos2d-X的...

    Cocos2d-x实战 C++卷,完整扫描版

    全书内容涵盖了Cocos2d-x的核心类、瓦片地图、物理引擎、音乐音效、数据持久化、网络通信、数据交换格式、内存管理、性能优化、平台移植、程序代码管理、三大应用商店发布产品等。本书共29章,按内容结构可分为六篇...

    Cocos2d-x的内存管理总结

    主要介绍了Cocos2d-x的内存管理总结,详解探讨了手工对象内存管理、自动对象内存管理、自动释放的时机等问题,需要的朋友可以参考下

    Learn iPhone and iPad cocos2d Game Development

     2.3 cocos2d中的内存管理问题  2.4 改变世界  2.5 你还应该知道的  2.5.1 ios设备  2.5.2 关于内存的使用  2.5.3 模拟器  2.5.4 关于日志  2.6 本章小结 第3章 基础知识  3.1 cocos2d中的单件类  3.2 ...

    cocos2d-xccobject

    ccobject cocos2d-xcocos2d-x最初移植自cocos2d的objective C版本。因此,在内存管理上,使用了和NSObject类似的引用计数器方法,相关接口放置在CCObject类中。

    Cocos2d-x开发中C++内存管理

    始并没有介绍C++语言,C++的内存管理当然也没进行不论什么的说明,为了掌握Cocos2d-x中的内存管理机制,是有必要先了解一些C++内存管理的知识。  C++内存管理非常复杂,假设全然地系统地介绍可能须要一本书的篇幅...

    剖析iOS开发中Cocos2d-x的内存管理相关操作

    主要介绍了剖析iOS开发中Cocos2d-x的内存管理相关操作,Cocos2d-x是开发游戏的利器,需要的朋友可以参考下

    modules-for-cocos2d-x:cocos2d-x 模块,如数据管理器和其他工具

    cocos2d-x版本:3.2 ##1. 单元测试 所有的模块的单元测试在test文件夹下。 ios,mac用户打开test/proj.ios_mac/test.xcodeproj, 修改部分引用路径,直接编译运行即可。 android用户可以将test/proj.andorid项目导入到...

    Cocos2d-x 3.x入门教程(二):Node节点类

    Node类是Cocos2d-x中一个非常重要的类,它继承自类Ref,关于Ref类,Ref类是一个内存管理的类,我后续也会总结的,这里就不做多说了。 Node类在Cocos2d-x中有多重要呢?任何需要画在屏幕上的对象都是节点类。最常用的...

    C++基础入门教程(五):new和delete

    对于以前没有接触过C++,然后初次接触Cocos2d-x的朋友来说,可能对于内存管理方面会比较生疏。 也经常会因为内存问题导致各种小Bug,我也曾经写过一篇retain和release倒底怎么玩?,用来驾驭Cocos2d-x的对象引用和...

Global site tag (gtag.js) - Google Analytics