cocos2d-iphone - 使用 CCAction 和 CCCallFunc/CCCallBlock 保留循环

标签 cocos2d-iphone automatic-ref-counting retain

我正处于发布第一款游戏的最后阶段,在运行 Instruments:Leaks & Allocations 后,我发现我的代码中存在由保留周期引起的泄漏。我正在使用 Cocos2d 2.0,并使用 ARC 编译我的应用程序,我应该提到的是,我在 ARC 之前启动了该项目,并使用 Xcode 重构工具对其进行转换。我的游戏每个屏幕有几个动画对象,每个对象都有少量(1-7)个该对象的动画“变体”(即谷仓打开一次显示一匹马,另一次显示斑马)。我有一个代表每个动画的类,还有一个代表每个变体的类。该变体从一系列帧创建一个 CCAnimation,然后创建一个 Action ,每当在正确区域接收到触摸事件时就会运行该 Action 。此操作是导致我的保留周期的原因。我对 ivar 操作的声明如下所示:

@interface AnimationVariant : NSObject
{
@private
    CCAction*  _action;
...
}
@property (readonly, nonatomic) CCAction* action;
...

-(void) setupActionWithMask:(int)mask
                     cycles:(int)cycles
                   hasScale:(bool)hasScale
                      scale:(float)scale
                masterScale:(float)master_scale
                  animationFrames:(NSArray*) frames
                   duration:(float)duration
                   andBlock:(VoidBlock)block;

@end

在 setupActionWithMask 方法的实现中,我构建了一个 CCActions 的 NSMutableArray,actionList。 CCActions 的顺序因参数而异,但通常看起来像这样:

[actionList addObject:[CCScaleTo actionWithDuration:0.0f scale:scale]];
[actionList addObject: [CCAnimate actionWithAnimation:animation] ];
[actionList addObject:[CCScaleTo actionWithDuration:0.0f scale:master_scale]];
[actionList addObject: [CCCallBlock actionWithBlock:block]];

我创建这样的操作:

_action = [CCSequence actionMutableArray:actionList];

使用类创建一个AnimationVariant实例,设置其属性,调用setupActionWithMask,并传入一个它想要在操作完成时执行的 block 。当使用类想要播放动画变体时,它会这样做:

[self runAction: variant.action];

我尝试将 _action 声明为:

CCAction* __unsafe_unretained _action;

这当然破坏了保留周期,但是操作被破坏了,并且在需要时不再存在(这正是您所期望的,因为 __unsafe_unretained 不保留)。我知道 __weak 是推荐的解决方案,但由于我的目标是 iOS 4 及更高版本,我认为它不适合我。

我的代码中有另一个保留周期,与这个完全一样,也是由保留(当然使用 ARC 自动)包含 CCCallFunc/CCCallBlock 的 CCSequence 引起的。我通过在需要时重新创建它来解决这个问题,在这种情况下我也可以这样做,但这些动画在整个游戏中可能会触发几百次,所以我希望遵循推荐的 Cocos2d 最佳实践并保留操作。

谢谢!

最佳答案

保留操作并不是最佳实践。这甚至不是一个好的做法。尽管它受到许多人的大力推荐,但不幸的是。

保留操作在许多情况下有效,但在其他情况下会失败,导致对象泄漏。我猜你的情况可能就是其中之一。

由于您的目标是 iOS 4,因此无法使用弱引用。但您可能应该重新考虑,除非您必须针对剩余的少数第一代和第二代设备。否则,谷歌搜索 iOS 5 的采用率。少数尚未更新的设备远低于合理阈值,特别是如果您认为这些用户可能不再购买(很多)应用程序。

既然您指的是CCCallFunc,请确保不使用它们并替换为CCCallBlock。 CCCallFunc 与 ARC 一起使用并不安全,特别是当您必须将数据对象 __bridge_transfer 转换为 void* 时(也是不好的做法)。

总是有可能永远不会发生必要的桥接转换回原始对象,然后 ARC 就没有机会清理该对象。使用 CCCallFunc,当您运行 call func 操作但该操作在调用回调选择器之前停止时,可能会发生这种情况,例如通过更改场景或停止操作/序列。

如果不遵循这条规则,Cocos2D 也容易出现循环引用:

  • 任何节点都应该只保留作为其子节点或孙子节点之一的另一个节点

在所有其他情况下(即节点保留(祖父)父节点或兄弟节点),您必须确保在 -(void) 清理方法中将这些引用置零。在 -(void) dealloc 中这样做已经太晚了,因为当存在保留周期时,对象将永远不会到达 dealloc。

关于cocos2d-iphone - 使用 CCAction 和 CCCallFunc/CCCallBlock 保留循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12873866/

相关文章:

objective-c - 使用 ARC 按需销毁对象

ios - NSMutableArray 内部的 block 泄漏 (ARC)

ios - 怎么把[[ivar keep] autorelease]转换成ARC?

reference - [NSStream scheduleInRunLoop : forMode:] retain the NSStream? 是否

ios - SpriteKit linearDamping - 最大?

cocos2d-iphone - 如何实现每帧运行的更新方法

ios - -[CCSprite draw] 530 中的 OpenGL 错误 0x0502

ios - ABAddressBook API 在大量使用该应用程序后停止工作?

objective-c - ios 在函数内部初始化实例以在外部使用

ios - CCMenuItemFont 无法改变颜色,始终为白色