CCCallFunc和ARC

时间:2012-09-13 04:28:28

标签: objective-c cocos2d-iphone

继承人我的问题我正在尝试为iphone的cocos2d中的动作序列创建一个回调函数,但我一直收到错误的访问错误。

我在这里创建了我的回电

id myCallFunc = [CCCallFuncND actionWithTarget:self.delegate selector:@selector(playerAttack:data:) data:(__bridge void *)([[PlayerAttackPacket alloc] initWithPlayer:@"" attackChoice: [NSNumber numberWithInt: item.tag]]) ]; // to call our method

这里是回调函数,在编译数据时编译器说错误访问。

-(void) playerAttack:(id)sender data:(void *)data
{
    PlayerAttackPacket* packet = (__bridge PlayerAttackPacket*)data;
    BattleModel *model = [BattleModel sharedInstance];
    int choice = packet.attackChoice.intValue;
    NSString * players = packet.player;
}

播放器包:

@interface PlayerAttackPacket : NSObject {
    NSString * player;
    NSNumber * attackChoice;
}

@property (nonatomic, retain) NSString * player;
@property (nonatomic, retain) NSNumber * attackChoice;
-(id) initWithPlayer: (NSString*)_player attackChoice: (NSNumber*)choice;
@end

@implementation PlayerAttackPacket
@synthesize player,attackChoice;

-(id) initWithPlayer: (NSString*)_player attackChoice: (NSNumber*)choice
{
    if((self=[super init]))
    {
        self.player = _player;
        self.attackChoice = choice;
    }
    return self;
}
@end

谁能告诉我我做错了什么? =(。我的感觉是它与ARC有关,但我不确定。

2 个答案:

答案 0 :(得分:5)

为了便于阅读,这里是你重新格式化的调用函数方法:

void* data = (__bridge void*)[[PlayerAttackPacket alloc] initWithPlayer:@"" 
                               attackChoice: [NSNumber numberWithInt: item.tag]];
id myCallFunc = [CCCallFuncND actionWithTarget:self.delegate 
           selector:@selector(playerAttack:data:) data:data ];

你做的是这个:

  • 分配新对象
  • 桥把它变成无效*
  • 将其传递给CCCallFuncND

ARC看到/做的是:

  • 分配了新对象
  • 新对象桥接演员(忽略)
  • 对象在调用func
  • 后超出范围
  • 发布对象

您没有对该对象保持强引用,因此在执行选择器时会释放它。请不要使用Ben的建议,我知道它有效,但它也很危险,因为它会在调用func动作实际上没有执行选择器时泄漏内存,即停止动作/序列或在调用时更改场景func仍在运行,等待执行选择器。

您可以通过两种方式解决此问题:

  • 保持对新对象的强引用,直到执行选择器,即作为类
  • 的实例变量
  • 改为使用CCCallBlock

一定要使用一个块!它避免了内存管理问题,您也不必对该对象有强引用。事实上,你甚至不必将它传递给块!以下是它的工作原理:

PlayerAttackPacket* packet = [[PlayerAttackPacket alloc] initWithPlayer:@"" 
                               attackChoice: [NSNumber numberWithInt: item.tag]];

id myCallBlock = [CCCallBlock actionWithBlock:^{

    // no bridge casting required, packet variable is accessible within the block
    // no memory management needed, block copies packet and keeps it alive
    BattleModel *model = [BattleModel sharedInstance];
    int choice = packet.attackChoice.intValue;
    NSString * players = packet.player;
           }];

现在我有点猜测,但在我看来,就像你创建了PlayerAttackPacket类一样,只是为了将几个参数传递给CCCallFuncND。您也可以使用块跳过它!

NSString* player = @"player1";

id myCallBlock = [CCCallBlock actionWithBlock:^{

    // whatever variables you need from the surrounding scope, you can just use
    // them as if they were local variables in the block!
    int choice = item.tag;
    NSString * players = player;
           }];

块非常方便,使用ARC可以更好地工作。使用块!重复:use blocks

答案 1 :(得分:0)

很难说,但我认为你必须在你的CCCallFuncND声明中更改(__bridge void *)到(_bridge_retained void *),并且当你把它转换回来时在playerAttack里面改变转换为(_bridge_transfer PlayerAttackPacket *)。

一个重要的注意事项是,如果您的场景在调用playerAttack方法之前结束,那么在CCSequence期间,数据包将永远不会被释放,因此会泄漏。此外,如果动作停止,则会出现LearnCocos2D指出的泄漏。

我建议您查看CCCallBlock操作,因为它们更容易使用,您不应该遇到此问题。