2

私は最初のゲームをリリースする最終段階にあり、Instruments:Leaks&Allocationsを実行した後、保持サイクルが原因でコードにリークがあることがわかりました。私はCocos2d2.0を使用しており、ARCを使用してアプリをコンパイルしています。プロジェクトをARCより前に開始し、Xcodeリファクタリングツールを使用して変換したことを述べておく必要があります。私のゲームには、画面ごとにいくつかのアニメーションオブジェクトがあり、それぞれにそのオブジェクトのアニメーション化された「バリアント」が少数(1〜7)あります(つまり、納屋が開いて馬とシマウマが表示されます)。各アニメーションを表すクラスと、バリアントごとに別のクラスがあります。バリアントは、一連のフレームからCCAnimationを作成し、タッチイベントが正しい領域で受信されるたびに実行されるアクションを作成します。このアクションが私の保持サイクルを引き起こしているものです。

@interface AnimationVariant : NSObject
{
@private
    CCAction*  _action;
...
}
@property (readonly, nonatomic) CCAction* action;
...

-(void) setupActionWithMask:(int)mask
                     cycles:(int)cycles
                   hasScale:(bool)hasScale
                      scale:(float)scale
                masterScale:(float)master_scale
                  animationFrames:(NSArray*) frames
                   duration:(float)duration
                   andBlock:(VoidBlock)block;

@end

setupActionWithMaskメソッドの実装では、CCActionsのNSMutableArray、actionListを作成します。CCActionのシーケンスは引数によって異なりますが、通常は次のようになります。

[actionList addObject:[CCScaleTo actionWithDuration:0.0f scale:scale]];
[actionList addObject: [CCAnimate actionWithAnimation:animation] ];
[actionList addObject:[CCScaleTo actionWithDuration:0.0f scale:master_scale]];
[actionList addObject: [CCCallBlock actionWithBlock:block]];

そして、私はこのようなアクションを作成します:

_action = [CCSequence actionMutableArray:actionList];

消費クラスは、AnimationVariantインスタンスを作成し、そのプロパティを設定し、setupActionWithMaskを呼び出し、アクションの完了時に実行するブロックを渡します。消費クラスがアニメーションバリアントを再生したい場合、次のように実行します。

[self runAction: variant.action];

_actionを次のように宣言してみました:

CCAction* __unsafe_unretained _action;

もちろん、これは保持サイクルを中断しましたが、アクションは破棄され、必要なときに実行されなくなります(__unsafe_unretainedは保持されないため、これは予想どおりです)。__weakが推奨される解決策であることは知っていますが、iOS 4以降をターゲットにしているため、利用できないと思います。

これとまったく同じように、コードに別の保持サイクルがありました。これも、CCCallFunc / CCCallBlockを含むCCSequenceを(もちろんARCで自動的に)保持することによって発生しました。必要なときにいつでも再作成することで解決しました。この場合も可能ですが、これらのアニメーションはゲーム全体で数百回トリガーされる可能性があるため、推奨されるCocos2dのベストプラクティスに従うことを望んでいました。アクションを保持します。

ありがとう!

4

1 に答える 1

2

アクションを保持することはベスト プラクティスではありません。それは良い習慣でもありません。多くの人に強く推奨されていますが、残念ながら.

多くの場合、アクションの保持は機能しますが、失敗してオブジェクトがリークする場合もあります。あなたのケースもその一つだと思います。

iOS 4 をターゲットにしているため、弱参照は使用できません。ただし、残りの数台の第 1 世代と第 2 世代のデバイスをターゲットにする必要がない限り、おそらく再検討する必要があります。それ以外の場合は、Google で iOS 5 の採用率を調べてください。まだ更新されていない一握りのデバイスは、妥当なしきい値を大きく下回っています。特に、それらのユーザーがおそらく (多くの) アプリを (もう) 購入しないと考える場合はなおさらです。

CCCallFunc を意味していたので、それらを使用しないようにして、CCCallBlock に置き換えてください。CCCallFunc を ARC で使用するのは安全ではありません。特に、データ オブジェクトを void* に __bridge_transfer キャストする必要がある場合 (これも悪い習慣です)。

元のオブジェクトへの必要なブリッジ キャスト バックが発生しない可能性が常にあり、その場合、ARC はそのオブジェクトをクリーンアップする機会を得られません。CCCallFunc を使用すると、呼び出し関数アクションを実行したときに、コールバック セレクターが呼び出される前にアクションが停止した場合に発生する可能性があります。たとえば、シーンの変更やアクション/シーケンスの停止によって発生します。

このルールに従わない場合、Cocos2D はサイクルを保持する傾向もあります。

  • どのノードも、その子または孫の 1 つである別のノードのみを保持する必要があります

他のすべての場合 (つまり、ノードが (グランド) 親または兄弟ノードを保持する) では、-(void) クリーンアップ メソッドでこれらの参照を nil にする必要があります。-(void) dealloc でこれを行うのは遅すぎます。これは、保持サイクルがある場合にオブジェクトが dealloc に到達しないためです。

于 2012-10-13T20:37:33.997 に答える