6

Using the sprite kit template that comes with Xcode, I modify the scene to be as follows :

#import "MyScene.h"

@interface MyScene ()
@property (nonatomic,strong)SKNode *floor;
@end

@implementation MyScene

-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */
    }
    return self;
}

-(void)update:(CFTimeInterval)currentTime {
    /* Called before each frame is rendered */
    [self removeAllChildren];
    self.floor = nil;
    self.floor = [SKNode node];

    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, nil, 0, 10);

    for(int i = 2; i<self.frame.size.width; i+=2)
    {
        CGPathAddLineToPoint(path, nil, i, 10);
    }

    self.floor.physicsBody = [SKPhysicsBody bodyWithEdgeChainFromPath:path];
    SKShapeNode *shape = [SKShapeNode node];
    shape.path = path;
    shape.strokeColor = [UIColor redColor];

    [self.floor addChild:shape];
    [self addChild:self.floor];

    CGPathRelease(path); 
}

@end

The app seems to keep using more memory, until it either hangs or crashes (after reaching about 180MB). Using the leaks and allocations tools, I have found the following:

Leaks: Screenshot of Leaks window Allocations: Screenshot of Allocations window

As can be seen from the images, there are a large number of Malloc calls using memory. I do not call Malloc directly - it seems these calls are made by SpriteKit. Likewise, there are a number of memory leaks, which also seem to be due to SKShapeNode, SKNode or other Sprite Kit objects.

How do I work around or solve this memory(leak) problem? I have to create SKShapeNodes, and SKNodes every frame. This code is just a sample to illustrate the problem - my actual project is much more complex with dynamically generated paths (not static like the one in this example).

Rahul Iyer
  • 19,924
  • 21
  • 96
  • 190
  • CFPathRelease indicates you aren't using ARC, correct? – CodeSmile Nov 29 '13 at 19:39
  • No. I use ARC. - This is a Sprite Kit project. – Rahul Iyer Nov 29 '13 at 19:43
  • CGMutablePathRef uses C API's, so you are responsible for releasing it. If you do not call CGMutablePathRelease, you will leak memory, and "Analyze" will warn you. – Rahul Iyer Nov 29 '13 at 19:50
  • Well ARC does complain about using CFRelease, but not the other release methods? – CodeSmile Nov 29 '13 at 23:54
  • Where do you see CFRelease ? I use only CGPathRelease which you have to use to release a CGPathMutableRef. ARC does not complain about it or anything else in my project. – Rahul Iyer Nov 30 '13 at 02:29
  • seems right, it once complainedto me about cfrelease and i made the assumption that arc also manages cf objects, but it does not – CodeSmile Nov 30 '13 at 08:55
  • I don't know why you keep bringing up CFRelease. It has nothing to do with me. :) – Rahul Iyer Nov 30 '13 at 09:36
  • cfrelease stands for cf*release in general. I'm just baffled that i missed the fact that arc doesn't manage cf objects, though it once complained to me about using cfrelease. Has nothing to do with the question. ;) – CodeSmile Dec 01 '13 at 09:54
  • Hey, I've analyzed my wrong answer, the question, these comments, and even read Learn's (Steffen's) article on why I should use ARC and I came to this conclusion: Disable Arc and use Cocos2D. That answers your bold text above ;) (For those super technically inclined people, I am joking) – Stephen J Dec 11 '13 at 19:53

4 Answers4

13

This is a bug in sprite kit. The problem isn't just with SKShapeNode, it is also with SKPhysicsBody.

Creating either a physics body, or a shape, using a CGPath causes a memory leak. You can verify this by commenting out either the physics body, or the shape, and running instruments with leaks, allocations and memory monitor.

Even if you release the path properly, sprite kit doesn't internally. Nothing you can do!

Fire up instruments, and watch the memory grow! XD

EDIT: This bug was present in iOS 7.0. Whether it has been fixed in 7.1 or later is not known to me as I stopped using SpriteKit because of this bug. One can verify if it is fixed by simply testing it.

Rahul Iyer
  • 19,924
  • 21
  • 96
  • 190
2

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 . 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.

prototypical
  • 6,731
  • 3
  • 24
  • 34
2

I'm using iOS 7.1.1 and had the same problem. However, by setting the SKShapeNode's path property to nil before removing a shape from a parent did fix this issue - no more leaks.

Tommy356
  • 252
  • 2
  • 7
0

I've the same problem.

Scene added one compound node (SKShape with SKSprite) then I removed this node from scene and added again(I got leak two nodes).

My leak is fixed with recreation compound node after remove from scene

Misha Vyrko
  • 992
  • 6
  • 14