我有一个实用函数,用于在圆形路径中移动 CCNode,无论是完整的圆还是部分圆。
该函数工作得非常好,但是如果我希望 CCNode 不断地跟随路径,我通过传入的 Block 来完成,最终调用相同的函数(有点递归,但不是真的)。
我发现的问题是,由于该函数在内部使用块,因此即使在调用 stopAllActions 或 removeFromParentAndCleanup:YES 之后,正在运行操作的 CCNode 也会保留,即使 CCNode 已被清理并从屏幕,它保留在内存中并且没有被释放。这似乎会影响性能,即使节点没有显示,因为 CCNode 和其他依赖项仍然(不知何故)在 cocos2d 系统中。
这是移动 CCNode 的函数:
@interface CocosUtil : NSObject {
}
typedef void (^NodeCompletionBlock)(CCNode *sprite);
+ (void) moveOperand:(CCNode*)operand throughCircleWithCentre:(CGPoint)centreOfElipse
startingDegrees:(float)startingDegrees
endingAtDegrees:(float)endingDegrees
startingRadius:(float)startingRadius
endingRadius:(float)endingRadius
withInitialDuration:(ccTime)initialDuration
withMainDuration:(ccTime)duration
clockwise:(BOOL)clockwise
completionBlock:(NodeCompletionBlock)handler;
@end
@implementation CocosUtil
+ (float) angleFromDegrees:(float)deg {
return fmodf((450.0 - deg), 360.0);
}
// Calculates the angle from one point to another, in radians.
//
+ (float) angleFromPoint:(CGPoint)from toPoint:(CGPoint)to {
CGPoint pnormal = ccpSub(to, from);
float radians = atan2f(pnormal.x, pnormal.y);
return radians;
}
+ (CGPoint) pointOnCircleWithCentre:(CGPoint)centerPt andRadius:(float)radius atDegrees:(float)degrees {
float x = radius + cos (CC_DEGREES_TO_RADIANS([self angleFromDegrees:degrees])) * radius;
float y = radius + sin (CC_DEGREES_TO_RADIANS([self angleFromDegrees:degrees])) * radius;
return ccpAdd(centerPt, ccpSub(CGPointMake(x, y), CGPointMake(radius, radius)));
}
+ (void) moveOperand:(CCNode*)operand throughCircleWithCentre:(CGPoint)centreOfElipse
startingDegrees:(float)startingDegrees
endingAtDegrees:(float)endingDegrees
startingRadius:(float)startingRadius
endingRadius:(float)endingRadius
withInitialDuration:(ccTime)initialDuration
withMainDuration:(ccTime)duration
clockwise:(BOOL)clockwise
completionBlock:(NodeCompletionBlock)handler {
float range;
if (clockwise == YES) {
if (endingDegrees <= startingDegrees) {
range = (360.0 + endingDegrees) - startingDegrees;
} else {
range = endingDegrees - startingDegrees;
}
} else {
if (endingDegrees >= startingDegrees) {
range = (360.0 + startingDegrees) - endingDegrees;
} else {
range = startingDegrees - endingDegrees;
}
}
__block float degrees = startingDegrees;
__block float radius = startingRadius;
const float incrementAngle = 10.0;
float intervals = (range / incrementAngle) - 1;
ccTime interval = duration / intervals;
float radiusStep = (endingRadius - startingRadius) / intervals;
if (clockwise == YES) {
degrees += incrementAngle;
} else {
degrees -= incrementAngle;
}
radius += radiusStep;
__block void (^moveToNextPoint)();
moveToNextPoint = [^(){
if (fabsf(degrees - endingDegrees) < 1.0) {
[operand runAction:[CCSequence actions:
[CCEaseBounceOut actionWithAction:
[CCMoveTo actionWithDuration:interval position:[self pointOnCircleWithCentre:centreOfElipse andRadius:radius atDegrees:degrees]]],
[CCCallBlock actionWithBlock:
^{
if (handler != nil) {
handler(operand);
}
}],
nil]];
} else {
[operand runAction:[CCSequence actions:
[CCMoveTo actionWithDuration:interval position:[self pointOnCircleWithCentre:centreOfElipse andRadius:radius atDegrees:degrees]],
[CCCallBlock actionWithBlock:moveToNextPoint],
nil]];
if (clockwise == YES) {
degrees += incrementAngle;
if (degrees > 360.0) {
degrees = degrees - 360.0;
}
} else {
degrees -= incrementAngle;
if (degrees < 0.0) {
degrees = degrees + 360.0;
}
}
radius += radiusStep;
}
} copy];
[operand runAction:[CCSequence actions:
[CCMoveTo actionWithDuration:initialDuration position:[self pointOnCircleWithCentre:centreOfElipse andRadius:startingRadius atDegrees:startingDegrees]],
[CCCallBlock actionWithBlock:moveToNextPoint],
nil]];
}
@end
您会注意到节点移动经过的弧被分成 10 度的步长。这样做是为了在不编写 CCActionInterval 子类的情况下获得圆周运动,但这意味着使用块或选择器来保持运动运行直到完成。
现在,为了让我的 CCNode 不断地移动一个完整的圆圈,我使用以下方法调用此函数:
- (void) moveLayer:(CCNode*)layer
startingDegrees:(float)startingDegrees
endingAtDegrees:(float)endingDegrees
startingRadius:(float)startingRadius
endingRadius:(float)endingRadius
withInitialDuration:(ccTime)initialDuration
withMainDuration:(ccTime)duration
clockwise:(BOOL)clockwise {
[CocosUtil moveOperand:layer throughCircleWithCentre:CGPointMake(240.0, 160.0) startingDegrees:startingDegrees endingAtDegrees:endingDegrees startingRadius:startingRadius endingRadius:endingRadius withInitialDuration:initialDuration withMainDuration:duration clockwise:clockwise completionBlock:^(CCNode *sprite) {
[self moveLayer:layer startingDegrees:startingDegrees endingAtDegrees:endingDegrees startingRadius:startingRadius endingRadius:endingRadius withInitialDuration:initialDuration withMainDuration:duration clockwise:clockwise];
}];
}
我尝试了一些不同的事情,比如根本不传入一个块,但是除了根本不使用该函数之外,没有什么能阻止保留。
据我所知,在 XCode 文档中阅读,我们有:
“如果在方法的实现中使用块,对象实例变量的内存管理规则会更加微妙:
如果通过引用访问实例变量,则保留 self;如果您按值访问实例变量,则会保留该变量。”
所以这告诉我,通过以我的方式在我的函数中使用块导致隐藏保留。
稍后调用 stopAllActions 不会触发释放。
这对我有用的唯一方法是,如果在我节点的 cleanup() 消息中添加[self release]
.
我不喜欢这样,因为它与执行保留的代码分离。
我的一个新想法是以某种方式将其重写为新的 CCActionInterval 子类,但我仍然不确定这是否能解决问题。
有什么建议么?