频道栏目
读书频道 > 移动开发 > 其他综合 > iOS 5 cocos2d 游戏开发实战(第2版)
2.3 cocos2d中的内存管理问题
2012-10-17 15:05:28     我来说两句
收藏   我要投稿
《iOS 5 cocos2d游戏开发实战(第2版)》将引导您开发富有吸引力的2D游戏。书中展示了如何使用cocos2d这款强大的游戏引擎来开发iPhone和iPad游戏,此外还介绍了游戏中的瓦片地图、虚拟摇杆、Game Center等。本书...  立即去当当网订购

本节将针对内存管理和自动释放消息的内容进行一些讨论。在采用引用计数机制的Objective-C中,内存管理遵守以下两条简单的规则:

●  如果你拥有(通过alloc、copy或retain得到)一个对象,就必须在用完之后释放它。

●  如果你已经向一个对象发送了自动释放消息,就不该再释放它。

通常来说,当在Objective-C中创建一个对象时会调用alloc方法。一旦调用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];

由于iOS 应用程序总是使用自动释放池,因此可以通过调用autorelease消息来避免发送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.

可见,autorelease 的出现从某种意义来说简化了内存管理问题,因为你再也不需要记着发送release消息了。自动释放池为你做到了这一点,它会在晚一些时候对池中的各个对象发送release消息。而增加autorelease消息对创建对象来说,也只是增添了一点点复杂度而已。

请看以下代码,它遵循常规风格来创建和释放CCNode对象:

// allocate a new instance of CCNode
CCNode* myNode = [[CCNode alloc] init];
// do something with myNode …
[myNode release];

大家普遍不倾向于使用这种创建cocos2d对象的方式。用静态初始化方法更容易一些,而且将返回一个会被自动释放的对象。与苹果官方推荐的方法相反,cocos2d 对于autorelease的使用已经内置到了引擎的设计中:它把诸如[[[NSObject alloc] init] utorelease]等调用写入了类的静态方法中。这是一件好事!这种设计让你不必再时刻盘算着哪些对象需要被释放,它使你避免了由于过度释放或内存泄漏而导致的程序崩溃风险。

CCNode类的静态初始化方法是“+(id)  node”。以下代码将向self发送alloc消息,这等效于在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 …

这就是使用自动释放对象的妙处。你不必再记着给对象发送释放消息。每一次cocos2d进入下一帧,那些不再使用的自动释放对象将被自动地释放。但这样做也有一个缺点:如果使用上述代码,然后在下一帧或者以后想要访问  myNode对象时,你就会发现它已经不在内存中了。如果这时发送消息给它,将导致程序出现EXC_BAD_ACCESS错误而崩溃。

简单地把CCNode* myNode变量当作类成员变量并不意味着对象使用的内存会被自动保留下来。如果想在下一帧或者以后的帧中访问自动释放对象,就必须保留它。并且,如果没有显式地将其添加为子节点,那么之后还是需要对其进行手动释放。

有一种可以更好地使用自动释放对象的方法,并且不需要显式地调用retain 方法——可以将生成的CCNode对象作为子节点添加到另一个派生自CCNode 的对象中,甚至可以删除成员变量而直接依赖cocos2d来保存对象:

// creating an autorelease instance of CCNode
-(void) init
{
myNode = [CCNode node];
myNode.tag = 123;
// adding the node as children to self
[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将CCNode对象添加到了一个集合中,本例使用了CCArray。CCArray与iOS SDK的NSMutableArray类似,但是效率比NSMutableArray更高。CCArray、NSMutableArray,还有iOS SDK中的任何其他集合都会自动地向每一个添加进来的对象发送retain消息,也会对每个要删除的对象发送release  消息。所以,这样生成的对象可以一直存在,并保持有效且可访问状态。但是,当它们从集合中删除以后,对象也会被自动释放。
 
上述方法对cocos2d对象来说是最好的内存管理方式。有些开发者可能会告诉你自动释放不好或者速度很慢,不可听信他们。

注意:

苹果官方的开发者文档建议减少使用自动释放对象,但是大多数  cocos2d 对象都是自动释放对象,这样能使内存管理更为简单。

如果使用alloc/init和release来管理每一个cocos2d对象,那么会遇到很多麻烦,而且收效甚微。我不是说你永远都不会用到alloc/init;它们确实是有用的,而且有时候你可能必须使用它们。但是对于  cocos2d 对象来说,应该依赖于静态的自动释放初始化方法。

自动释放对象只有一个缺点,那就是在游戏进入下一帧前,这些对象将一直占用内存。这就意味着,如果你在每一帧都生成许多很快就要被丢弃的自动释放对象,可能会浪费很多内存。不过这样的情况很少发生。

对于cocos2d内存管理的基本介绍就是这些。有关内存管理更深入的讨论,请参考苹果公司官方的Memory Management Programming Guide:https://developer.apple.com/iphone/library/
documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html。

您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:2.2.4 HelloWorld类
下一篇:2.4 改变世界
相关文章
图文推荐
排行
热门
最新书评
特别推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站