0

私はどういうわけか愚かな間違いを犯したと確信していますが、それを修正することはできないようです. CCScene のサブクラスを取得しました。このサブクラスには、層として cclayer のサブクラスがあり、大まかに次のようになります。

@interface MyLayer : CCLayer {
}

// some methods

@end

@interface MyScene : CCScene {

    MyLayer *_myLayer;

}

// some methods

@end

を構築する際に、次のことを行います。

-(id) init {
    if (self = [super init]) {
        _myLayer = [MyLayer node];
        [self addChild:_myLayer];

        // more stuff
    }
}

レイヤーと対話する必要があるため、_myLayer で参照が必要です。ただし、これにより保持カウントが 2 になります (_myLayer で 1 回、シーンの子ノードとして 1 回)。少なくとも私が理解している限りでは、これまでのところ問題はありません。しかし、これはまた、私がそれを解放しなければならないことを意味します。したがって、MyScene の割り当て解除は次のようになります。

-(void) dealloc {

    [_myLayer release];
    _myLayer = nil;

    [super dealloc];

}

実行時にシーンをリリースすると、すべて正常に動作します。リリースのプロセスをたどったところ、問題なくレイヤーを含むシーン全体をリリースできました。MyScene::release が 1 回呼び出され、アクティブに [_myLayer release] を呼び出すと、保持カウントが 1 減ります。MyLayer::release が 1 回呼び出されます ([super dealloc] - CCNode 内のすべての子を削除します)。

ただし、ゲーム全体を終了し (デバイス上のアプリを強制終了)、CCDirector::end が呼び出されるとすぐに、すべてが壊れます。実際には、_myLayer を 2 回解放しようとするためです。子供たちを解放することによって。

どちらの場合も同じである場合、何らかの間違いを犯したことを理解できましたが、そうではありません。期待どおりに機能したら-最初のケースでは保持カウントを1つ下げてから、別の方法で機能したら解放します。そして、なぜそれが事実なのか、私にはわかりません。

[_myLayer リリース] をまとめて廃棄してみました。その場合、_myLayer は実行時にまったく解放されませんが、シャットダウン時にはすべて正常に機能します。ここではある程度一貫していますが、それは私にとってはあまり役に立ちません。

4

2 に答える 2

2

まず第一に:retainCount役に立たない

これは、MyLayer の自動解放されたインスタンスを返します。

_myLayer = [MyLayer node];

これは次と同等です。

_myLayer = [[[MyLayer alloc] init] autorelease];

他のコードなしでそのままにしておくと_myLayer、 init メソッドが戻った後しばらくして、 がダングリング ポインターになります。次回自動解放プールがパージされるのは間違いなく、私の記憶が正しければ、cocos2d のすべてのフレームで発生します。ARC では、ivar 自体がデフォルトで強い参照になるため、レイヤーは保持され、期待どおりに割り当て解除されません。したがって、autorelease とその動作が気に入らない場合は、ARC を使用してください。;)

次に、レイヤーを子として追加します。これにより、レイヤーが配列に配置されます。つまり、レイヤーが保持されます。

[self addChild:_myLayer];

そのため、レイヤーがシーンの子である限り、割り当ては解除されません。

そして今、あなたの前の多くの人と同じように、レイヤーが解放されないという問題を修正するために間違った場所を見ていました. これを dealloc で行うと、余分なリリースが追加されます。

[_myLayer release];

実際の問題はレイヤーが解放されないことであり、ここで強制的に解放されるため、これは今のところ問題なく機能します。ただし、しばらくすると、シーンの子配列が解放され、各オブジェクトに解放が送信され、レイヤーの過剰解放によるクラッシュが発生します。

したがって、追跡する必要がある実際の問題は、レイヤーが割り当て解除されない理由です。そして、ここで私はより多くの問題を感じます:

シーン全体を解放できます

それが現場にメッセージを送っていたという意味ならrelease、それは間違っています。そしてまた、これはシーンを過剰に解放します。replaceScene または同様のメソッドを呼び出すと、Cocos2d はシーンを消去します。シーン自体も通常は自動解放されます。これは、nodeまたはsceneクラス メソッドを介して作成された場合に確実です。

それがあなたがしていることではなく、レイヤーが解放されない場合は、保持サイクルがあるかどうかを確認してください。それとも、レイヤーがシーンの前に割り当て解除されることを期待しているだけですか? それは必ずしもこの順序である必要はありません。

2 つ (またはそれ以上) の兄弟ノードを互いに保持する (つまり、レイヤー A がレイヤー B を保持し、レイヤー B がレイヤー A を保持する) ことによって、または保持された参照を保持する _myLayer などの親を保持する兄弟によって保持サイクルを簡単に作成できます。シーンに(ところで、self.parentを介してアクセスするのと同じです)。

保持サイクルを除いて、これらすべての問題がほぼ瞬時に解消されるため、ARCを使用するように言っています。しかし、リテイン サイクルの場合は、弱い参照をゼロにするという非常に単純で効果的な解決策を提供します。

たとえば、レイヤー参照をインターフェイスで弱いとして宣言すると、レイヤーが保持されることを心配する必要がなくなります。

 __weak MyLayer *_myLayer;

さらに、レイヤーが解放されると、_myLayer は自動的に nil に設定されます。そしてこれは、 の場合のように後で発生するのではなく、レイヤーに強い参照がなくなった場合に発生しautoreleaseます。

肯定的な副作用は、次のことを安全に実行できるようになったことです。

@interface LayerA : CCLayer
@property (weak) LayerB* layerB;
@end

@interface LayerB : CCLayer
@property (weak) LayerA* layerA;
@end

MRC では、適切にレイヤーを割り当て、dealloc メソッドのにレイヤーを nil に設定しないと、保持サイクルが作成されます。リテイン サイクルの厄介な点は、dealloc メソッド内で解決できないことです。これは、定義上、リテイン サイクルに参加しているすべてのオブジェクトが割り当て解除されないためです。cocos2d でこれを行う典型的な場所はクリーンアップ メソッドです。

しかし、ARC ではまったく問題ありません。他のレイヤーの参照が保持されていない (弱い) ため、いずれかまたは両方のレイヤーの割り当てが解除されます。いずれかのレイヤーの割り当てが解除されると、参照は nil に設定されるため、クラッシュは発生しません。

私は 2 年間、ARC のみで開発を行ってきました。振り返ることはありませんでした。メモリ管理に関連するエラーの割り当てがほぼゼロになりました。ばかげています。私がときどき調べなければならない唯一のことは、弱参照が nil になるとは思わないときに nil になることです。通常、それは、オブジェクトがどこかに強い参照を持っていると誤って想定しているときに、そうではありません。

ARC を使用すると非常に時間の節約になるので、これを学びたいと思っているとしても、Objective-C 開発の昔のメモリ管理がどのように機能していたかに完全に興味を持ったほうがよいでしょう。おばあちゃんがまだ最初の iPhone のコードを書いていたときのようにね! :)

PS: ARC を使用するときにメモリ管理の制御を放棄するというのは神話です。たとえ短時間であっても、コントロールを取り戻すためにできる巧妙なトリックがたくさんあります。主にブリッジキャスティングを使用。したがって、ARC がさらに数サイクルかかり、それがタイトなループになる可能性がある場合でも、MRC を使用してコードを手動で最適化できます (必要な場合は、おそらく必要ないでしょう)。これはこの回答の範囲を超えています(私はすでに行き過ぎました)が、これらのオプションは存在しますが、それらを実行する必要はほとんどありません。

これが、私が ARC を使用することに強引である理由です。IMO。

于 2013-10-30T16:03:19.037 に答える