2

这是我的问题我正在尝试为 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 有关,但我不确定。

4

2 回答 2

5

为了便于阅读,这里是您重新格式化的 call func 方法:

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 一起工作得更好。使用积木!重复:使用块

于 2012-09-13T09:17:41.457 回答
0

很难说,但我认为你必须在你的 CCCallFuncND 声明和 playerAttack 中将 (__bridge void *) 更改为 (_bridge_retained void*),当你将它转换为 (_bridge_transfer PlayerAttackPacket*) 时。

需要注意的是,如果你的场景在调用 playerAttack 方法之前就结束了,那么在 CCSequence 期间,数据包永远不会被释放,因此会泄漏。此外,如 LearnCocos2D 所指出的,如果该操作停止,则会出现泄漏。

我建议您查看 CCCallBlock 操作,因为它们更易于使用并且您不应该遇到这个问题。

于 2012-09-13T05:03:57.723 回答