2012-01-03 14 views
0

私は、次の書籍http://www.apress.com/9781430233039に続いて、iphone用のcocos2dを学ぼうとしています。ソースコード(http://www.apress.com/downloadable/download/sample/sample_id/640/)のCH_08フォルダにあるShootEmUp3サンプルを変更しました。iphone用のcocos2dの割り当てシステムの問題点

私が達成したいのは、メニューシーンとゲームシーンを持ち、ゲームシーンが終了すると(たとえばすべてのライフが失われた)メニューシーンに戻ることができることです。メニューシーンからは、 "LevelIcon"というクラスのアイコンであるアイコンをクリックすることで、特定のGameSceneにアクセスできます。言い換えると、 "Navigator"(CCLayerのサブクラス)と呼ばれるメニューシーンは、メニューシーンに現れる "LevelIcon"クラス(NSObjectのサブクラス)と、CCDirectorのreplaceSceneメソッドを呼び出すことによってタッチイベントに応答すること、 次のように:。

//From LevelIcon.m  
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event 
{ 
    CCLOG(@"Touch"); 
    [[CCTouchDispatcher sharedDispatcher] removeDelegate:self]; 
    GameScene * game = [GameScene scene]; 
    [[CCDirector sharedDirector] replaceScene:game]; 
    return TRUE; 
} 

私は、ナビゲーターのdeallocメソッドでオブジェクトを解放しようとしましたが、私はreleaseメソッドが呼び出されないことが判明LevelIconの解除方法にCCLOGメッセージを追加します。

//From Navigator.m 

-(void)dealloc 
{ 
    CCLOG(@"Navigator dealloc"); 
    CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self); 

    level1 = nil; 
    [level1 dealloc]; 

    [super dealloc]; 
} 

私はその後、更新方法でログを追加し、それはまだシーンがGameSceneなくナビゲーターシーンであっても呼ばれていた観察。私が理解できないことは、LevelIconがNavigatorで作成されたオブジェクトであり、Navigatorのdeallocメソッドでは、上に示したクラスのistanceを解放しようとしました(ですが、はしません)。

本の確認として、私はGameSceneに次のコードをトリガするためにGameSceneからのナビゲーターに戻すと、前方に行くことを試みた:

 Navigator * navigator = [Navigator scene]; 
    [[CCDirector sharedDirector] replaceScene:navigator]; 

また、新しいナビゲータークラスの作成が適切に動作しますが、 GameSceneに戻ると、新しいLevelIconクラスインスタンスは消えず、残っています(累積するように)。このイベントの後ナビゲーター解除方法はLevelIconと複数の場合と呼ばれていない、つまり

GameScene * game = [GameScene scene]; 
[[CCDirector sharedDirector] replaceScene:game]; 

を:私たちは、次のようreplaceScene方法は、タッチイベントに応答LevelIconクラス内から呼び出されていることに気づくしなければなりませんLevelIconクラスはメモリ内でアクティブなままです。

私は間違っていますが、いくつかのヒントや助けに感謝します。

また、(addChildメソッドを使用して)Navigatorの子としてLevelIconのistanceを追加することで、別のアプローチを試みましたが、NSObjectクラスから派生し、cocos2d Nodeクラスから派生しているため動作しません。何とかこれを調整するのが理にかなっているのか、2つの異なるクラス階層(cocos2dとNS/Objective-C)のように肉ではないのか疑問に思っていました。

私は理解を助けるために、完全なコードを貼り付け:読書のための

--NAVIGATOR.H----- 

#import <Foundation/Foundation.h> 
#import "cocos2d.h" 
#import "LevelIcon.h" 

@interface Navigator : CCLayer { 
    LevelIcon *level1; 
} 

+ (id) scene; 

@end 


-----NAVIGATOR.M------ 
#import "Navigator.h" 
#import "LevelIcon.h" 

@implementation Navigator 

+(id) scene { 
    CCScene *scene = [CCScene node]; 
    CCLayer *layer = [Navigator node];//?? 
    [scene addChild:layer]; 
    return scene;  
} 

-(id)init 
{ 
    CCLOG(@"init"); 
    if((self=[super init])){   
     CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self); 
     self.isAccelerometerEnabled=YES; 
     self.isTouchEnabled = YES; 

     [self scheduleUpdate]; 
     level1 = [LevelIcon levelIconWithParentNode:self]; 
    } 
    return self; 
} 

-(void)dealloc 
{ 
    CCLOG(@"Navigator dealloc"); 
    CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self); 

    level1 = nil; 
    [level1 dealloc]; 

    [super dealloc]; 
} 
[..] 
@end 


------LEVELICON.H------------ 
#import <Foundation/Foundation.h> 
#import "cocos2d.h" 

@interface LevelIcon : NSObject<CCTargetedTouchDelegate> { 
    CCSprite* levelIconSprite; 
} 

+(id) levelIconWithParentNode:(CCNode*)parentNode; 
-(id) initWithParentNode:(CCNode*)parentNode; 

@end 



-----LEVEL ICON.M---------------- 
#import "LevelIcon.h" 
#import "GameScene.h" 

@implementation LevelIcon 

// Static autorelease initializer, mimics cocos2d's memory allocation scheme. 
+(id) levelIconWithParentNode:(CCNode*)parentNode 
{ 
    CCLOG(@"levelIconWithParentNode"); 
    return [[[self alloc] initWithParentNode:parentNode] autorelease]; 
} 

-(id) initWithParentNode:(CCNode*)parentNode 
{ 
    CCLOG(@"initWithParentNode"); 
    if ((self = [super init])) 
    { 
     CCLOG(@"initWithParentNode: inside if"); 
     CGSize screenSize = [[CCDirector sharedDirector] winSize]; 

     levelIconSprite = [CCSprite spriteWithFile:@"Icon.png"]; 
     levelIconSprite.position = CGPointMake(CCRANDOM_0_1() * screenSize.width, CCRANDOM_0_1() * screenSize.height); 
     [parentNode addChild:levelIconSprite]; 

     // Manually schedule update via the undocumented CCScheduler class used internally by CCNode. 
     [[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:0 paused:NO]; 

     // Manually add this class as receiver of targeted touch events. 
     [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:-1 swallowsTouches:YES]; 
    } 

    return self; 
} 

-(void) dealloc 
{ 
    CCLOG(@"Level icon dealloc"); 
    // Must manually unschedule, it is not done automatically for us. 
    [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self]; 

    // Must manually remove this class as touch input receiver! 
    [[CCTouchDispatcher sharedDispatcher] removeDelegate:self]; 
    [super dealloc]; 
} 

-(void) update:(ccTime)delta 
{ 
    CCLOG(@"Icon Update!"); 
} 


-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event 
{ 
    CCLOG(@"Touch"); 
    [[CCTouchDispatcher sharedDispatcher] removeDelegate:self]; 
    GameScene * game = [GameScene scene]; 
    [[CCDirector sharedDirector] replaceScene:game]; 
    return TRUE; 
} 

@end 

感謝を!:)

答えて

0

私は、エラーが直接リリースオブジェクトの呼び出しによって引き起こされたものではないことを発見しました。

上記のコードはautoreleaseの初期化を示しているため、リリースを呼び出す必要はありません。私は同意する、私の側にナビゲータで再びリリースを呼び出すことによってエラーがあった。これについての言い訳はありません(私にはこれについての私の無知ですが、成功なしのビフォーなしでそれをやろうとしたことは、ほとんど知られていない「理論」を信じずにリリースで間違った方法で遊んでいきます。より重要な方法で問題を見つける)。

問題はオブジェクトが「何らかの形で」2度保持されていたため、deallocが呼び出されていないことでした。この問題を解決するには

可能性保持が失われていたであろうように、私は、ccTouchBegan方法に

[[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self]; 
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self]; 

への呼び出しを追加しました。方法removeDelegateの定義において

が引数として渡されたオブジェクトを保持ccArrayAppendObjectへの呼び出しがあるsharedDispatcher addTargetedDelegate方法における明示的な解放する呼が、 存在しない(以下のコードを参照)。

それから私は sharedDispatcher] removeDelegateがある handlersToRemoveのリストにオブジェクトを追加することを観察している今 ccTouchBeganに呼び出され removeDelegate(以下のコードを参照してください)関数の定義を見て
//From CCTouchDispatcher.m line 121 
-(void) addTargetedDelegate:(id<CCTargetedTouchDelegate>) delegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches 
{ 
    CCTouchHandler *handler = [CCTargetedTouchHandler handlerWithDelegate:delegate priority:priority swallowsTouches:swallowsTouches]; 
    if(! locked) { 
     [self forceAddHandler:handler array:targetedHandlers]; 
    } else { 
     [handlersToAdd addObject:handler]; 
     toAdd = YES; 
    } 
} 

//From ccCArray.h line 115 (after having opened the definition of several function starting from **addObject** being called by addTargetedDelegate I got here) 
/** Appends an object. Bahaviour undefined if array doesn't have enough capacity. */ 
static inline void ccArrayAppendObject(ccArray *arr, id object) 
{ 
    arr->arr[arr->num] = [object retain]; 
    arr->num++; 
} 

解放された。これはオブジェクトが解放される原因になります。

//From LevelIcon.m 
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event 
{ 
    CCLOG(@"Touch"); 
    [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self]; 
    [[CCTouchDispatcher sharedDispatcher] removeDelegate:self]; 

    GameScene * game = [GameScene scene]; 
    [[CCDirector sharedDirector] replaceScene:game]; 



    return TRUE; 
} 

//From CCTouchDispatcher.m line 153 
-(void) removeDelegate:(id) delegate 
{ 
    if(delegate == nil) 
     return; 

    if(! locked) { 
     [self forceRemoveDelegate:delegate]; 
    } else { 
     [handlersToRemove addObject:delegate]; 
     toRemove = YES; 
    } 
} 
2

これはかなり奇妙です:

level1 = nil; 
[level1 dealloc]; 

1)あなたは、直接のdealloc呼び出したい[LEVEL1リリース]を使用して、参照カウントが0に達したとき、それは自動的に

をdealloc'edされますしないでください

2)この順序は間違っています。level1をnilに設定すると、次の行は無効になります(無関係にメッセージをnilポインタに送信できますが、何もしません)。

+0

こんにちは、投稿していただきありがとうございます。私はNavi deallocメソッドを変更して、無割り当てを取り除き、 '[level1 release];を呼び出しました;' LevelIcon releaseメソッドを2回呼び出すときにコードがクラッシュするようになりました。 '(2012-01-03 14:48:54.506 ShootEmUp [6120 :207] Navigator dealloc 2012-01-03 14:48:54.507 ShootEmUp [6120:207] dealloc: 2012-01-03 14:48:54.507 ShootEmUp [6120:207 ]レベルアイコンdealloc 2012-01-03 14:48:54.508 ShootEmUp [6120:207]レベルアイコンdealloc) ' – mm24

+0

また、{[CCTouchDispatcher sharedDispatcher] removeDelegate:self];'をccTouchBeganメソッドから削除しようとしました。 LevelIconは、それがダブルアロケーションを引き起こしたかもしれないと思っていますが、そうするときには(上に掲示されているコードに示されているようにdeallocでのみコールを残します。 – mm24

関連する問題