1

これは恥ずべきことですが、そうです。ARCを使用していても、これに問題があります。

私の主な質問は次のとおりです。コードに循環参照がありますか?

私のコードの説明:

一連の子ノード(background、playerEntity、inputlayer)を持つゲームシーンがあります。問題は、ゲームオーバーをトリガーしてシーンを置き換えると、シーンの割り当てが解除されないことです(ARCを使用しているため、必要がない場合でも、ゲームシーンにdeallocメソッドを追加しました)。

これは、子ノードではゲームシーンを参照し、ゲームシーンではそれらを参照して循環参照を作成しているためだと思います。

たとえば、ゲームシーンの更新メソッドでは、次のように呼び出します。

-(void) update:(ccTime)delta
{
bool isGamePaused = [GameController sharedGameController].isGamePaused;

if(isGameOverInProgress==true){
    //Do whatever game over animation u like
}
else if(isGamePaused==false)
{
    elapsedTime+=delta;

    //Update input layer
    InputLayerButtons *inputLayer = (InputLayerButtons *) [self getChildByTag:InputLayerTag];
    [inputLayer update:delta];

    //Move background
    ParallaxMultipleBackgroundOneBatchNode* background = (ParallaxMultipleBackgroundOneBatchNode*) [self getChildByTag:BackgroundNodeTag];
    [background update:delta];

    //verify enemies bullet collision
    [self verifyPlayerBulletCollision];
    [levelSprites update:delta];

    //Update bullets position
    [bulletCache updateVisibleBullets:delta];

これは一種のクレイジーなアプローチですが、ゲームで起こっていることをShooterSceneクラスで完全に制御できるようにするために、子ノードで更新をスケジュールすることは避けたかったのです。ShooterSceneクラスにはセミシングルトンパターンを使用していません。代わりに、ノード間でゲームの状態を伝達するために使用するセミシングルトンGameControllerクラスインスタンスがあります。

しかし、一部の子ノードでは、親のShooterSceneを参照する必要がありました。たとえば、セミシングルトンGameControllerクラスを使用して船の位置を保存すると、応答が遅すぎることが判明しました(たとえば、updateメソッドで位置を読み取り、それに応じて船を更新すると、はるかに遅くなります。ここで行うように直接更新するよりも)。したがって、私のアプローチは、私が望んでいたように100%クリーンではありません(循環参照の問題を回避し、ShooterSceneクラスのすべてのイベントの制御を維持するために、親クラスへのすべての参照を避けたかった)。

したがって、例として、入力レイヤー(つまりInputLayerButtons)から船の位置を更新する方法は次のとおりです。

-(void) update:(ccTime)delta
{
    // Moving the ship with the thumbstick.
    ShooterScene * shooterScene = (ShooterScene *) [self parent];
    ShipEntity *ship = [shooterScene playerShip];
    delta = delta * 2.0f;

    CGPoint velocity = ccpMult(joystick.velocity, 200);
    if (velocity.x != 0 && velocity.y != 0)
    {
        ship.position = CGPointMake(ship.position.x + velocity.x * delta, ship.position.y + velocity.y * delta);
    }     
}

親シーンを参照しても大丈夫であり、循環参照にはならないだろうと思いました。しかし、それは起こっているように見えるので、私はこれについて混乱しています。

これがゲームオーバーメソッドのコードです。ShooterSceneのdeallocメソッドは呼び出されません(代わりにクリーンアップが呼び出されます)。

   [[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:4.0 scene:[MainMenuScene scene] withColor:ccc3(255, 255, 255)]];

replaceSceneに私が理解できない何かがありますか?replaceObjectAtIndexのコードが見つからなかったので、ここでは、replaceSceneのCocos2d2.0コードのみを投稿します。

-(void) replaceScene: (CCScene*) scene
{
    NSAssert( scene != nil, @"Argument must be non-nil");

    NSUInteger index = [scenesStack_ count];

    sendCleanupToScene_ = YES;
    [scenesStack_ replaceObjectAtIndex:index-1 withObject:scene];
    nextScene_ = scene; // nextScene_ is a weak ref
}

他のシーン(ShooterSceneよりも単純で、潜在的な循環参照がない)でdeallocメソッドが呼び出されるかどうかを確認しようとしましたが、機能しました。循環参照によるものだと確信していますが、これを引き起こすコードをどのように検出できますか?

4

1 に答える 1

2

ARCは保持サイクルを妨げません。ただし、弱参照をゼロにすることで、保持サイクルを作成する可能性が大幅に減少します。

cocos2dノード階層と保持サイクルに関する一般的な経験則として:

次の場合、保持サイクルはありません。

  • 子(または孫)ノードへの強力な参照を維持する

はい、それがcocos2dの保持サイクルに関して安全な唯一のルールです!つまり、ノードの子または孫ではないノードへの参照は、強力な(保持)参照であってはなりません。

次の場合、保持サイクルが保証されます。

  • 親(または祖父母)ノードへの強力な参照を維持する
  • 兄弟ノードへの強力な参照を保持し、兄弟ノードは直接または間接的に元のノードへの強力な参照を保持します

次の場合、保持サイクルが発生する可能性があります。

  • そのノードが元のノードまたは他の要因への強い参照を保持しているかどうかに応じて、子(または孫)ノードではないノードへの強い参照を保持します。

あなたの場合、それはあなたが保持サイクルを作成した親への強い参照を維持することによって意味します。ARCでは、デフォルトですべてのivarが強力な参照になります。これと他の潜在的な保持サイクルを解決するには、2つのことを行うことができます。

  • プロパティをweakに変更するか、ivarを__weakに変更します(最小のデプロイメントターゲットとしてiOS 5.0が必要です-強くお勧めします)
  • ノードの-(void)クリーンアップメソッドでivarをnilに設定します

弱参照を作成する方法:

// weak references to parent nodes are safe:
@interface MyClass : CCNode
{
    __weak CCLayer* _gameLayer;
}
@property (nonatomic, weak) CCScene* gameScene;
@end

適切にクリーンアップする方法:

-(void) cleanup
{
    _strongRefToNonChildNode = nil;
    [super cleanup];
}

-(void) momCallsShesAlmostHere
{
    [self cleanupApartment];  // just kidding :)
}

適切と思われるdeallocでのクリーンアップは、保持サイクルを解決するには遅すぎます。なぜなら、保持サイクルある場合、保持サイクルによってオブジェクトの割り当てが最初から解除されないため、明らかにそれをdeallocで解決することはできません。

于 2012-10-16T13:16:55.490 に答える