使用CCAction和CCCallFunc / CCCallBlock保留循环

时间:2012-10-13 14:07:05

标签: cocos2d-iphone automatic-ref-counting retain

我正处于发布我的第一个游戏的最后阶段,并且在运行了Instruments:Leaks&分配,我看到由于保留周期导致我的代码泄漏。我正在使用Cocos2d 2.0,并使用ARC编译我的应用程序,我应该提到我在ARC之前启动了项目,并使用Xcode重构工具进行转换。我的游戏每个屏幕有几个动画对象,每个动画对象都有一小部分(1-7)该对象的动画“变体”(即谷仓打开以显示一次马和斑马另一次)。我有一个代表每个动画的类,以及每个变体的另一个类。变体从一系列帧创建CCAnimation,然后创建一个动作,只要在正确的区域中接收到触摸事件,该动作就会运行。这个动作是导致我的保留周期的原因。我对行动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的顺序取决于args,但通常看起来像这样:

[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,并在操作完成时传入它想要执行的块。当消费类想要播放动画变体时,它会这样:

[self runAction: variant.action];

我尝试将_action声明为:

CCAction* __unsafe_unretained _action;

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

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

谢谢!

1 个答案:

答案 0 :(得分:2)

保留行动不是最佳做法。这甚至都不是很好的做法。虽然很多人都非常推荐它,但很不幸。

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

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

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

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

如果您不遵守此规则,Cocos2D也容易保留周期:

  • 任何节点都应该只保留另一个节点,即其子节点或孙子节点

在所有其他情况下(即节点保留(祖父)父节点或兄弟节点),您必须确保在 - (void)清理方法中将这些引用设为nil。在 - (void)dealloc中这样做太迟了,因为当有一个保留周期时,对象永远不会被释放。