Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SKShapeNode has unbounded memory growth

If I run this code in a SKScene sublass init method

for (int i = 0; i < 100; i++) {

            SKShapeNode *shape = [SKShapeNode node];

            shape.antialiased = NO;

            CGMutablePathRef path = CGPathCreateMutable();

            CGPathAddEllipseInRect(path, NULL, CGRectMake(arc4random()%320, arc4random()%320, 10, 10));

            shape.path = path;

            [shape setStrokeColor:[UIColor blackColor]];

            CGPathRelease(path);

            [self addChild:shape];

            [shape removeFromParent];

        }

and everytime I run this code in my SKView controlling controller

SKView * skView = (SKView *)self.view;

// Create and configure the scene.

SKScene * scene = [ZTMyScene sceneWithSize:skView.bounds.size];

scene.scaleMode = SKSceneScaleModeAspectFill;



// Present the scene.

[skView presentScene:scene];

my memory usage grow until the memory is full and crashes. This does not happen if I use a SKSpriteNode. Has anyone a fix for this?

Summary: I create the sprite kit template project, add a lot of SKShapeNodes and replace the old SKScene with a new one.

I added a sample project to github https://github.com/zeiteisen/MemoryTest

like image 722
zeiteisen Avatar asked Sep 19 '13 08:09

zeiteisen


1 Answers

I became aware of this issue while reading another post. I messed around with SKShapeNode a bit and indeed verified the memory leak issue pinpointed here.

While doing that, I had an idea...

Not really a new idea, more of a repurposed one. This wonderful idea actually allowed me to use SKShapeNodes to my hearts content :)

POOLING

Yep... I just created a pool of SKShapeNodes that I reused as needed. What a difference that makes :)

You simply redefine the path whenever needed, when done using return to your pool, and it'll be waiting there for you to play with again at a later time.

Create a ivar or property NSMutableArray in your SKScene called pool and create it when you init the SKScene. You can either populate the array with your shape nodes during init, or you can create them as needed.

This is something quick method I created for grabbing a new node from the pool :

-(SKShapeNode *)getShapeNode
{
    if (pool.count > 0)
    {
        SKShapeNode *shape = pool[0];
        [pool removeObject:shape];
        return shape;
    }

    // if there is not any nodes left in the pool, create a new one to return
    SKShapeNode *shape = [SKShapeNode node];

    return shape;
}

So wherever in the scene you need a SKShapeNode you'd do this :

SKShapeNode *shape = [self getShapeNode];
// do whatever you need to do with the instance

When you are done using the shape node, just return it to the pool and set the path to NULL, for example :

[pool addObject:shape];
[shape removeFromParent];
shape.path = NULL;

I know it's a workaround and not an ideal solution, but certainly this is a very viable workaround for anyone wanting to use a large number of SKShapeNodes and not bleed memory.

like image 130
prototypical Avatar answered Oct 10 '22 08:10

prototypical