OK. Taking the advice from @LearnCocos2D, and doing what I was already thinking of doing here is my solution for others that may want it.
Basically, I rewrote the entire thing as a relatively simple CCActionInterval subclass.
There are no longer any blocks involved at all, and thus, no hidden retains. Much cleaner, and much, much more elegant (I think).
The interface:
#import "cocos2d.h"
@interface CCMoveThroughArc : CCActionInterval
/** creates the action */
+(id) actionWithDuration:(ccTime)duration
centre:(CGPoint)centreOfCircle
startingAtDegrees:(float)startingDegrees
endingAtDegrees:(float)endingDegrees
startingAtRadius:(float)startingRadius
endingAtRadius:(float)endingRadius;
/** creates the action */
+(id) actionWithDuration:(ccTime)duration
centre:(CGPoint)centreOfCircle
startingAtDegrees:(float)startingDegrees
endingAtDegrees:(float)endingDegrees
startingAtRadius:(float)startingRadius
endingAtRadius:(float)endingRadius
reversed:(BOOL)reversed;
/** initializes the action */
-(id) initWithDuration:(ccTime)duration
centre:(CGPoint)centreOfCircle
startingAtDegrees:(float)startingDegrees
endingAtDegrees:(float)endingDegrees
startingAtRadius:(float)startingRadius
endingAtRadius:(float)endingRadius
reversed:(BOOL)reversed;
@end
The implementation:
#import "CCMoveThroughArc.h"
@implementation CCMoveThroughArc {
CGPoint centre_;
float startingDegrees_;
float endingDegrees_;
float diffDegrees_;
float startingRadius_;
float endingRadius_;
float diffRadius_;
BOOL reversed_;
}
/** creates the action */
+(id) actionWithDuration:(ccTime)duration
centre:(CGPoint)centreOfCircle
startingAtDegrees:(float)startingDegrees
endingAtDegrees:(float)endingDegrees
startingAtRadius:(float)startingRadius
endingAtRadius:(float)endingRadius {
return [[self alloc] initWithDuration:duration centre:centreOfCircle startingAtDegrees:startingDegrees endingAtDegrees:endingDegrees startingAtRadius:startingRadius endingAtRadius:endingRadius reversed:NO];
}
/** creates the action */
+(id) actionWithDuration:(ccTime)duration
centre:(CGPoint)centreOfCircle
startingAtDegrees:(float)startingDegrees
endingAtDegrees:(float)endingDegrees
startingAtRadius:(float)startingRadius
endingAtRadius:(float)endingRadius
reversed:(BOOL)reversed {
return [[self alloc] initWithDuration:duration centre:centreOfCircle startingAtDegrees:startingDegrees endingAtDegrees:endingDegrees startingAtRadius:startingRadius endingAtRadius:endingRadius reversed:reversed];
}
/** initializes the action */
-(id) initWithDuration:(ccTime)duration
centre:(CGPoint)centreOfCircle
startingAtDegrees:(float)startingDegrees
endingAtDegrees:(float)endingDegrees
startingAtRadius:(float)startingRadius
endingAtRadius:(float)endingRadius
reversed:(BOOL)reversed {
if( (self=[super initWithDuration:duration]) ) {
centre_ = centreOfCircle;
startingDegrees_ = fminf(startingDegrees, endingDegrees);
endingDegrees_ = fmaxf(startingDegrees, endingDegrees);
startingRadius_ = startingRadius;
endingRadius_ = endingRadius;
reversed_ = reversed;
diffDegrees_ = endingDegrees_ - startingDegrees_;
diffRadius_ = endingRadius_ - startingRadius_;
}
return self;
}
-(id) copyWithZone: (NSZone*) zone
{
CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] centre:centre_ startingAtDegrees:startingDegrees_ endingAtDegrees:endingDegrees_ startingAtRadius:startingRadius_ endingAtRadius:endingRadius_ reversed:reversed_];
return copy;
}
-(CCActionInterval*) reverse
{
return [[self class] actionWithDuration:duration_ centre:centre_ startingAtDegrees:startingDegrees_ endingAtDegrees:endingDegrees_ startingAtRadius:startingRadius_ endingAtRadius:endingRadius_ reversed:!reversed_];
}
-(void) startWithTarget:(CCNode *)aTarget
{
NSAssert1(([aTarget isKindOfClass:[CCNode class]] == YES), @"CCMoveThroughArc requires a CCNode as target. Got a %@", [[aTarget class] description]);
[super startWithTarget:aTarget];
}
- (float) angleFromDegrees:(float)deg {
return fmodf((450.0 - deg), 360.0);
}
- (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) update:(ccTime) t {
float angle;
float radius;
if (reversed_ == NO) {
angle = (diffDegrees_ * t) + startingDegrees_;
radius = (diffRadius_ * t) + startingRadius_;
} else {
angle = endingDegrees_ - (diffDegrees_ * t);
radius = (diffRadius_ * (1.0 - t)) + startingRadius_;
}
CGPoint pos = [self pointOnCircleWithCentre:centre_ andRadius:radius atDegrees:angle];
[(CCNode*)target_ setPosition:pos];
}
@end
And the code that uses it:
- (void) moveStartingAtDegrees:(float)startingDegrees
endingAtDegrees:(float)endingDegrees
startingRadius:(float)startingRadius
endingRadius:(float)endingRadius
withInitialDuration:(ccTime)initialDuration
withMainDuration:(ccTime)duration {
[self runAction:[CCRepeatForever actionWithAction:
[CCMoveThroughArc actionWithDuration:duration centre:CGPointZero startingAtDegrees:startingDegrees endingAtDegrees:endingDegrees startingAtRadius:startingRadius endingAtRadius:endingRadius reversed:YES]]];
}
I hope that this is of some use to others. It certainly helped me. It doesn't truly answer my actual question, but it deals with the problem behind the question very effectively.