cocos2d 中的内存管理问题 本节将针对内存管理和自动释放消息的内容进行一些讨论。在采用引用计数机制的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] autorelease]等调用写入了类的静态方法中。这是一件好事!这种设计让你不必再时刻盘算着哪些对象需要被释放,它使你避免了由于过度释放或内存泄漏而导致的程序崩溃风险。 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 对象来说,应该依赖于静态的自动释放初始化方法。自动释放对象只有一个缺点,那就是在游戏进入下一帧前,这些对象将一直占用内存。这就意味着,如果你在每一帧都生成许多很快就要被丢弃的自动释放对象,可能会浪费很多内存。不过这样的情况很少发生。 ……