SKPhysicsBody bodyWithPolygonFromPath memory leaks

前端 未结 5 1913
醉酒成梦
醉酒成梦 2020-12-10 15:38

I have a strange memory leaks when creating Sprite Kit physics bodies with custom shapes. This is how my implementation looks:

CGFloat offsetX = self.frame.s         


        
5条回答
  •  一生所求
    2020-12-10 16:03

    As commented in several places, this looks like a bug in the implementation of SKPhysicsBody, which persists at least until iOS 7.1. The reason for this is:

    SKPhysicsBody holds an instance variable, '_path', which holds a copy of the initial CGPathRef passed when calling 'bodyWithEdgeChainFromPath' or similiar constructors. This instance variable never gets released, so all paths will stay in memory.

    However, you may implement a workaround for this by

    (1) subclassing the SKShapeNode, that should hold the SKPhysicsBody,

    (2) after creating and assigning the SKPhysicsBody for this node, retrieve the instance variable referring to the CGPathRef of the SKPhysicsBody,

    (3) when the shape node is deallocated, check the retain count of the path. If it's > 0, release it and the memory leaks are gone.

    There's little code overhead (beside subclassing all shape nodes, that use physics bodies relying on CGPath's). Just do this:

    Add an instance variable to your subclass:

    {
        CGPathRef myPath;
    }
    

    Implement the method to retrieve the value for this CGPath in any subclassed SKShapeNode implementation. You might also consider to add this as a general category on SKNode:

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

    This code will return the CGPathRef instance used by the node's physics body. Note, however, that this must be done immediately after assigning the physics body to the node. At a later time (i.e. in dealloc(), this might return a null value. So, after creating the body, store this value in the instance variable 'myPath'. To make this code work even after Apple might fix the bug, we'll add an additonal retain, which will make sure we can access this object once our SKNode is deallocated (see below):

        /*
         *  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]);
    

    Finally, overwrite the 'dealloc' method and release the path, after your SKNode is released:

    - (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);
            }
        }
    }
    

    This will finally release this path and fix the memory leaks. This code works for all iOS version up to 7.1 and it also should work on further versions, once Apple finally fixes this bug and SKPhysicsBoy actually releases the path (as they are supposed to do).

提交回复
热议问题