2012-09-13 50 views
2

繼承人我的問題我想創建一個回調函數爲iphone的cocos2d中的動作序列,但我不斷收到錯誤的訪問錯誤。CCCallFunc和ARC

這裏創建我回電話

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有關,但我不確定。

+0

你從哪裏得到錯誤的訪問異常?你在哪裏運行CCCallFuncND? –

+0

首次訪問「數據」時,playerAttack發生訪問不良(請參閱上文)。 CCCallFunND在CCSequence中並在其結尾被調用。 – AwDogsGo2Heaven

回答

5

爲了增強可讀性,這裏是你的電話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後超出範圍
  • 發行對象

您不保留對該對象的強引用,因此它在執行選擇器時釋放。請不要使用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

+0

謝謝!你對這個數據包是正確的,因爲我想傳遞幾個參數。但也想到我可能需要它,因爲當我實施與Game Center的戰鬥(我沒有看到它,但也許我沒有)。順便說一下,我閱讀你的教程很多。他們是一個很好的幫助。 – AwDogsGo2Heaven

+0

爲CCCallBlock解決方案投票... soooooo好多了 – staticfiction

0

很難說,但我認爲你必須在你的CCCallFuncND中更改(__bridge void *)爲(_bridge_retained void *)聲明和內部playerAttack當你把它轉換回(_bridge_transfer PlayerAttackPacket *)

一個重要的注意事項是,如果你的場景結束之前,你的playerAttack方法被調用,所以在CCSequence,數據包永遠不會如果停止操作,LearnCocos2D指出會有泄漏

我建議您查看CCCallBlock操作,因爲它們更容易操作o使用,你不應該有這個問題。

+0

no no no no no!不要在那裏使用__bridge_retained。如果你要停止這個動作,它會泄漏內存,因爲你無法通過__bridge_transfer來匹配它。 __bridge_retained不能在Core Foundation類的任何位置使用,也不能在您完全控制傳輸對象的生命週期的任何地方使用。 – LearnCocos2D

+0

你是對的。這是一個重大缺陷,我在我的回答中提到了這一點,但還不夠好。反正有沒有保留對象,否則雖然或將代碼需要完全更改爲不使用CCCallFuncND? –

+1

在調用選擇器之前,您必須保持對該對象的強引用。實例變量工作正常,然後在選擇器中將其零。儘管如此,與使用塊相比,它仍然是一種不幸。我有點希望cocos2d 2.0做了正確的事情,並刪除了整個CCCallFunc操作,以支持它們的塊對應。 – LearnCocos2D