9

カスタム シェイプを使用してスプライト キットの物理ボディを作成すると、奇妙なメモリ リークが発生します。これは私の実装がどのように見えるかです:

CGFloat offsetX = self.frame.size.width * self.anchorPoint.x;
CGFloat offsetY = self.frame.size.height * self.anchorPoint.y;

CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 4 - offsetX, 3 - offsetY);
CGPathAddLineToPoint(path, NULL, 66 - offsetX, 3 - offsetY);
CGPathAddLineToPoint(path, NULL, 35 - offsetX, 57 - offsetY);
CGPathCloseSubpath(path);

self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path];

CGPathRelease(path);

すべてがSKSpriteNodeメソッド内で行われています。そのようなボディを作成した後、Instruments はいくつかのメモリ リークについて教えてくれます。

Leaked object: 
  Malloc 32 Bytes
Size:
  32 Bytes
Responsible Library: 
  PhysicsKit
Responsible Frame:
  std::__1::__split_buffer<PKPoint, std::__1::allocator<PKPoint>&>::__split_buffer(unsigned long, unsigned long, std::__1::allocator<PKPoint>&)

CGPathRelease(path);行が必要です-それがないと、理解できるメモリリークが増えますCGPath。代わりにこの実装を使用している場合 (テスト目的):

CGFloat radius = MAX(self.frame.size.width, self.frame.size.height) * 0.5f;
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:radius];

...すべてが正常に機能しており、メモリ リークは発生していません。これは Sprite Kit のバグなのか、何か間違っているのだろうか。

4

5 に答える 5

4

これはスプライト キットのバグです。修正を待つ必要があります。

于 2013-12-05T08:38:25.150 に答える
2

pathパラメータの唯一の制限は次のとおりです。

反時計回りに曲がり、自己交差のない凸多角形パス。ポイントは、所有ノードの原点を基準にして指定されます。

offsetXandの値がoffsetYわからないのでパスが正しいかどうかわかりませんが、両方とも 0 であると仮定すると、このパスは時計回りで反時計回りではないように思えます。パスが正しいことを確認するために、変数を使用せずに定数を使用してパスを作成します。それでもリークが発生する場合は、 のバグであると言えPhysicsKitます。

于 2013-11-29T22:10:33.453 に答える
2

いくつかの場所でコメントされているように、これは少なくとも iOS 7.1 まで続く SKPhysicsBody の実装のバグのようです。その理由は次のとおりです。

SKPhysicsBody はインスタンス変数 '_path' を保持します。これは、'bodyWithEdgeChainFromPath' または同様のコンストラクターを呼び出すときに渡される初期 CGPathRef のコピーを保持します。このインスタンス変数は解放されないため、すべてのパスがメモリに残ります。

ただし、次の方法で回避策を実装できます。

(1) SKPhysicsBody を保持する必要がある SKShapeNode のサブクラス化、

(2) このノードの SKPhysicsBody を作成して割り当てた後、SKPhysicsBody の CGPathRef を参照するインスタンス変数を取得します。

(3) 形状ノードが解放されたら、パスの保持回数を確認します。> 0 の場合は解放すると、メモリ リークはなくなります。

コードのオーバーヘッドはほとんどありません (CGPath に依存する物理ボディを使用するすべてのシェイプ ノードをサブクラス化する以外に)。これを行うだけです:

サブクラスにインスタンス変数を追加します。

{
    CGPathRef myPath;
}

サブクラス化された SKShapeNode 実装で、この CGPath の値を取得するメソッドを実装します。これを SKNode の一般的なカテゴリとして追加することも検討してください。

- (CGPathRef) getPhysicsBodyPath
{
    CGPathRef path = nil;
    if ( self.physicsBody ){
        object_getInstanceVariable(self.physicsBody, "_path", (void**) &path);
    }
    return(path);
}

このコードは、ノードの物理ボディによって使用される CGPathRef インスタンスを返します。ただし、これは物理ボディをノードに割り当てた直後に行う必要があることに注意してください。後で (dealloc() で)、これは null 値を返す可能性があります。そのため、本体を作成した後、この値をインスタンス変数 'myPath' に格納します。Apple がバグを修正した後でもこのコードを機能させるには、追加の保持を追加します。これにより、SKNode の割り当てが解除されると、このオブジェクトに確実にアクセスできるようになります (以下を参照)。

    /*
     *  we need to keep a copy of the final path reference, that has been created for the physics body.
     *  retrieving this value during deallocation won't work any more...
     */
    myPath = CFRetain([self getPhysicsBodyPath]);

最後に、SKNode が解放された後、「dealloc」メソッドを上書きしてパスを解放します。

- (void) dealloc
{
    self.physicsBody = nil;
    [super dealloc];
    if ( myPath != nil ) {
        /* this will work, because we've retained the path for this instance */
        CFIndex rc = CFGetRetainCount (myPath);
        /* this is our own release ... */
        CGPathRelease(myPath);
        /* in case Apple has fixed the release, this is OK, otherwise we'll need
         * to release the path twice to avoid memory leaks
         */
        if ( rc > 1 ) {
            CGPathRelease(myPath);
        }
    }
}

これにより、最終的にこのパスが解放され、メモリ リークが修正されます。このコードは 7.1 までのすべての iOS バージョンで機能し、Apple が最終的にこのバグを修正し、SKPhysicsBoy が実際にパスをリリースすると、それ以降のバージョンでも機能するはずです (想定どおり)。

于 2014-07-26T16:49:30.083 に答える
2

以下を実行すると何か変わりますか?

CGPathRef pathCopy = CGPathCreateCopy(path);
CGPathRelease(path);
self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:pathCopy];
CGPathRelease(pathCopy);
于 2013-11-29T22:07:19.880 に答える