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 ];

あなたがすることはこれです:

  • 新しいオブジェクトを割り当てる
  • ブリッジで void* にキャスト
  • それを CCCallFuncND に渡します

ARCが見たり行ったりすることは次のとおりです。

  • 新しいオブジェクトが割り当てられました
  • 新しいオブジェクト ブリッジ キャスト (無視)
  • 関数の呼び出し後にオブジェクトが範囲外になる
  • オブジェクトを解放する

オブジェクトへの強い参照を保持しないため、セレクターが実行されるまでに解放されます。ベンのアドバイスを使用しないでください。動作することはわかっていますが、呼び出し関数アクションが実際にセレクターを実行しないとき、つまりアクション/シーケンスを停止するとき、または呼び出し中にシーンを変更するときはいつでもメモリリークするという点でも危険です。 func はまだ実行中で、セレクターの実行を待機しています。

これは、次の 2 つの方法で解決できます。

  • セレクターが実行されるまで、つまりクラスのインスタンス変数として、新しいオブジェクトへの強い参照を保持します
  • 代わりに 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;
           }];

ここで推測ですが、いくつかのパラメーターを CCCallFuncND に渡すためだけに PlayerAttackPacket クラスを作成したように思えます。ブロックでもスキップできます!

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 宣言で (__bridge void *) を (_bridge_retained void*) に変更する必要があると思います。

重要な注意点として、playerAttack メソッドが呼び出される前にシーンが終了すると、CCSequence 中にデータ パケットが解放されず、リークが発生します。また、アクションが停止すると、LearnCocos2D が指摘したようにリークが発生します。

CCCallBlock アクションの方がはるかに使いやすく、この問題は発生しないはずなので、チェックアウトすることをお勧めします。

于 2012-09-13T05:03:57.723 に答える