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

Cocos2d iphone 使用CCAction和CCCallFunc/CCCallBlock保留循环,cocos2d-iphone,automatic-ref-counting,retain,Cocos2d Iphone,Automatic Ref Counting,Retain,我正处于发布第一个游戏的最后阶段,在运行Instruments:Leaks&Allocations之后,我发现我的代码中有一个由保留周期引起的漏洞。我正在使用cocos2d2.0,并使用ARC编译我的应用程序,我应该提到我在ARC之前启动了这个项目,并使用Xcode重构工具对其进行了转换。我的游戏在每个屏幕上都有几个动画对象,每个对象都有一小部分(1-7个)该对象的动画“变体”(即谷仓打开一次显示一匹马,另一次显示一只斑马)。我有一个代表每个动画的类,还有一个代表每个变体的类。该变体从一系列帧创

我正处于发布第一个游戏的最后阶段,在运行Instruments:Leaks&Allocations之后,我发现我的代码中有一个由保留周期引起的漏洞。我正在使用cocos2d2.0,并使用ARC编译我的应用程序,我应该提到我在ARC之前启动了这个项目,并使用Xcode重构工具对其进行了转换。我的游戏在每个屏幕上都有几个动画对象,每个对象都有一小部分(1-7个)该对象的动画“变体”(即谷仓打开一次显示一匹马,另一次显示一只斑马)。我有一个代表每个动画的类,还有一个代表每个变体的类。该变体从一系列帧创建一个动画,然后创建一个动作,该动作将在正确区域接收到触摸事件时运行。此操作是导致我的保留周期的原因。我对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
[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];
[self runAction: variant.action];
在setupActionWithMask方法的实现中,我构建了一个NSMutableCCActions数组actionList。CCAction的顺序因参数而异,但通常看起来如下所示:

@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
[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];
[self runAction: variant.action];
我创造了这样的动作:

@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
[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];
[self runAction: variant.action];
消费类创建AnimationVariant实例,设置其属性,调用setupActionWithMask,并在动作完成时传入它希望执行的块。当消费类想要播放动画变体时,它会这样做:

@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
[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];
[self runAction: variant.action];
我试图将_行动声明为:

CCAction* __unsafe_unretained _action;
这当然打破了保留周期,但操作被销毁,并且在需要时不再存在(这是您所期望的,因为uu unsafe u unrepaired不会保留)。我知道_;弱是推荐的解决方案,但由于我的目标是iOS 4及更高版本,我认为它不适合我

我的代码中有另一个retain循环,与此完全相同,也是由保留(当然是自动使用ARC)包含CCCallFunc/CCCallBlock的CCSequence引起的。我解决了这个问题,只要在我需要的时候重新创建它,在这个例子中我也可以这样做,但是这些动画在整个游戏中可能会触发几百次,所以我希望遵循建议的Cocos2d最佳实践并保留动作


谢谢

保留行动不是最佳做法。这甚至都不是好的做法。尽管它被很多人大力推荐,但很不幸

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

因为您的目标是iOS 4,所以不能使用弱引用。但您可能应该重新考虑,除非您必须针对剩余的少数第一代和第二代设备。否则,谷歌对iOS 5的采用率将下降。一些尚未更新的设备远远低于合理的阈值,特别是如果你认为这些用户可能不购买(很多)应用程序(无论如何)。 因为您的意思是CCCallFunc,所以请确保不使用它们并替换为CCCallBlock。CCCallFunc与ARC一起使用是不安全的,尤其是当您必须uuu bridge u transfer将数据对象强制转换为void*(这也是一种糟糕的做法)

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

如果不遵循以下规则,Cocos2D也容易保留周期:

  • 任何节点应仅保留其子节点或子节点之一的另一个节点

在所有其他情况下(即节点保留(大)父节点或同级节点),必须确保在-(void)cleanup方法中禁用这些引用。在-(void)dealloc中这样做太晚了,因为当存在保留周期时,对象将永远无法到达dealloc。

非常感谢。我已经得出了相同的结论,并重写了代码,以完全不保留操作,并且看不到性能下降(这不是我真正期望的)。我不知道-(void)清理方法-不确定我怎么会错过它。但我确实试图在dealloc中取消ivar,正如你所说的,那时已经太晚了。我已经切换到了块-但我也没有意识到CCCallFunc对于ARC是不安全的-我只是认为块更干净。实际上,你根本无法在Xcode 4.5上为第一代和第二代设备(armv6设备)开发。最低目标版本是4.3。@newacct-是的,几天后我去买二手的第二代Touch进行测试时发现了这一点。但是看起来你可以将目标定为4.3,它仍然不支持弱引用——除非我弄错了。