12

I have updated iPad Air to 7.1 and Xcode to 5.1. Xcode wanted to update my project to recommended settings, I agreed.

After that my game began crashing in a couple of places when I remove node from parent.

That was a surprise for me — there were no crossings before the update. I restored my project and found out what Xcode does to it — only changes Architectures string:

Before: Architectures before update

After: Architectures after update

In the old version there are no crashes. There are no crashes if I remove arm64 support in new version. In the simulator there are no crashes in both new and old versions.

Where should I pay attention in my code?

Code stack:

SpriteKit`SKCSprite::removeSubsprite(SKCSprite*):
0x1859442cc:  stp    fp, lr, [sp, #-16]!
0x1859442d0:  add    fp, sp, 0
0x1859442d4:  stp    x20, x19, [sp, #-16]!
0x1859442d8:  sub    sp, sp, #16
0x1859442dc:  mov    x19, x0
0x1859442e0:  str    x1, [sp, #8]
0x1859442e4:  add    x20, sp, 8
0x1859442e8:  add    x0, x19, 544
0x1859442ec:  mov    x1, x20
0x1859442f0:  bl     0x18594872c               ; unsigned long std::__1::__tree<SKCSprite*, std::__1::less<SKCSprite*>, std::__1::allocator<SKCSprite*> >::__erase_unique<SKCSprite*>(SKCSprite* const&)
0x1859442f4:  add    x0, x19, 464
0x1859442f8:  mov    x1, x20
0x1859442fc:  bl     0x185948218               ; std::__1::list<SKCSprite*, std::__1::allocator<SKCSprite*> >::remove(SKCSprite* const&)
0x185944300:  ldr    x20, [sp, 1]
0x185944304:  ldrb   w8, [x20, #18]
0x185944308:  ldrh   w9, [x20, #16]
0x18594430c:  orr    w8, w9, w8, lsl #16
0x185944310:  tbnz   w8, #1, 0x185944324       ; SKCSprite::removeSubsprite(SKCSprite*) + 88
0x185944314:  ldr    x9, [x20, 61]
0x185944318:  ldr    x9, [x9, 2]
0x18594431c:  cbnz   x9, 0x185944324           ; SKCSprite::removeSubsprite(SKCSprite*) + 88
0x185944320:  tbz    w8, #8, 0x185944330       ; SKCSprite::removeSubsprite(SKCSprite*) + 100
0x185944324:  mov    x0, x19
0x185944328:  mov    x1, x20
0x18594432c:  bl     0x18594828c               ; SKCSprite::removeFromOffscreenList(SKCSprite*)
0x185944330:  str    xzr, [x20, #392]
0x185944334:  ldr    x8, [x20, 0]
0x185944338:  ldr    x8, [x8, 10]
0x18594433c:  mov    x0, x20
0x185944340:  blr    x8
0x185944344:  ldrh   w8, [x19, #20]
0x185944348:  orr    w9, w8, #0x40
0x18594434c:  strh   w9, [x19, #20]
0x185944350:  ldr    x8, [x19, 49]
0x185944354:  cbz    x8, 0x185944388           ; SKCSprite::removeSubsprite(SKCSprite*) + 188
0x185944358:  add    x9, x19, 392
0x18594435c:  ldrh   w10, [x8, #20]
0x185944360:  orr    w10, w10, #0x40
0x185944364:  strh   w10, [x8, #20]
0x185944368:  ldr    x8, [x9, 0]
0x18594436c:  add    x9, x8, 392
0x185944370:  ldrh   w10, [x8, #20]
0x185944374:  orr    w10, w10, #0x40
0x185944378:  strh   w10, [x8, #20]
0x18594437c:  ldr    x8, [x8, 49]
0x185944380:  cbnz   x8, 0x18594435c           ; SKCSprite::removeSubsprite(SKCSprite*) + 144
0x185944384:  ldrh   w9, [x19, #20]  EXC_BAD_ACCESS here.
0x185944388:  orr    w8, w9, #0x2
0x18594438c:  strh   w8, [x19, #20]
0x185944390:  b      0x185944398               ; SKCSprite::removeSubsprite(SKCSprite*) + 204
0x185944394:  ldrh   w8, [x19, #20]
0x185944398:  tbnz   w8, #7, 0x1859443ac       ; SKCSprite::removeSubsprite(SKCSprite*) + 224
0x18594439c:  orr    w8, w8, #0x80
0x1859443a0:  strh   w8, [x19, #20]
0x1859443a4:  ldr    x19, [x19, 49]
0x1859443a8:  cbnz   x19, 0x185944394          ; SKCSprite::removeSubsprite(SKCSprite*) + 200
0x1859443ac:  sub    sp, fp, #16
0x1859443b0:  ldp    x20, x19, [sp], #16
0x1859443b4:  ldp    fp, lr, [sp], #16
0x1859443b8:  ret    lr

UPDATE: New information:

My error:

Error

Stack:

Call stack

I found out something wrong. My node I want to remove consists of two SKSpriteNodes (my sprite image and its shadow) and one SKShapeNode (a thin black rope). I tried to replace my SKShapeNode rope with a SKSpriteNode rope and I got success — problem gone.

Code to remove my node from parent:

SKAction *moveCloud = [SKAction moveToX:destinationX duration:moveDuration];
[cloud runAction:moveCloud completion:^{
    [cloud removeFromParent];
}];

Code to add a SKShapeNode rope to my node:

- (void)addRopeToCloud:(SKNode *)cloud
{
    CGFloat maxY = self.scene.size.height;
    CGFloat length = maxY - [self.scene convertPoint:cloud.position fromNode:cloud.parent].y;

    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, 0, 0);
    CGPathAddLineToPoint(path, NULL, 0, length);

    SKShapeNode *rope = [[SKShapeNode alloc] init];
    rope.path = path;
    rope.strokeColor = [UIColor blackColor];
    rope.lineWidth = 0.1;
    rope.antialiased = YES;
    rope.zPosition = -0.01;

    CGFloat threadScale = 1 / cloud.xScale;

    rope.xScale = threadScale;
    rope.yScale = threadScale;

    [cloud addChild:rope];
    CGPathRelease(path);
}

Code to add a SKSpriteNode rope to my node, that resolves the problem:

- (void)addRopeToCloud:(SKNode *)cloud
{
    CGFloat maxY = self.scene.size.height;
    CGFloat length = maxY - [self.scene convertPoint:cloud.position fromNode:cloud.parent].y;

    CGSize ropeSize = CGSizeMake(1.5, length);
    SKSpriteNode *rope = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:ropeSize];
    rope.anchorPoint = CGPointMake(0.5, 0);

    CGFloat ropeScale = 1 / cloud.xScale;
    rope.xScale = ropeScale;
    rope.yScale = ropeScale;

    rope.zPosition = -0.01;

    [cloud addChild:rope];
}

It looks like something wrong in my SKShapeNode adding code, but I can't understand what exactly is.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Alexander Bekert
  • 617
  • 8
  • 18
  • 1
    could you post the call stack + the code/method that initiates the remove from parent? Also post the actual error message. Add an exception breakpoint if you haven't yet. – CodeSmile Mar 14 '14 at 08:52
  • You've already said with your title that your game will crash on removeFromParent. So what else do you need to know? – El Tomato Mar 14 '14 at 09:21
  • 2
    FWIW I get a similar/same crash removing a node with a SKShapeNode child node. It always happens when I enable debug drawing which adds a SKShapeNode to every node. This looks like a bug. – CodeSmile Mar 14 '14 at 10:16
  • I have being struggling for few nights trying to understand what happened and why am I getting EXC_BAD_ACCESS, I was trying everything and drilled down to SKCSprite::removeSubsprite - now after seeing this post, at least I will have a good night sleep tonight – vim Mar 16 '14 at 18:19
  • This might be a bug in Sprite Kit. [Have you filed it with Apple?](http://bugreport.apple.com) – rickster Mar 20 '14 at 16:56
  • @AlexanderBekert, is it possible to get a link to the bug? I have a hacky work around. I've been adding my SKShapeNode to an NSMutableArray so that XCode doesn't attempt to release the object. Yes, this introduces a memory leak, but at least my app doesn't crash immediately on SKShapeNode release. – shad Apr 21 '14 at 16:13
  • Unfortunately not — you can see only cases you've created. I can recommend you to report the same bug. I think, that more cases provide the higher priority. – Alexander Bekert Apr 22 '14 at 19:17
  • @AlexanderBekert, I solved this problem. I had a __weak reference to my SKShapeNode in another object. I removed that and the crashing stopped. I thought that I needed the __weak reference to prevent circular references from occurring in my program, but I did a 5 minute run of my app and no memory leaks seemed to have been introduced. – shad Apr 29 '14 at 18:21

5 Answers5

4

It seems to only happen on iOS 7.1. Not sure if it is an Apple bug, but this seems to fix it:

- (void)removeFromParent
{
    [self.aShapeNode removeFromParent];
    self.aShapeNode = nil;

    [super removeFromParent];
}
dragosaur
  • 818
  • 1
  • 8
  • 10
  • Yep! This code should work. But you need to subclass SKNode to override removeFromParent. – Alexander Bekert Mar 21 '14 at 09:58
  • Do you do this in the SKNode subclass? I don't get what self.aShapeNode would be within the subclass. – Nic Hubbard Mar 27 '14 at 04:13
  • How does this work from within the subclassed ShapeNode? For me it would be: – zeeple Apr 10 '14 at 20:20
  • - (void)removeFromParent { [self removeFromParent]; self = nil; [super removeFromParent]; } – zeeple Apr 10 '14 at 20:21
  • But the problem I have is that the line: self = nil; generates an error that self cannot be assigned from outside init. How should I rework that overridden method? – zeeple Apr 10 '14 at 20:23
  • 1
    Implemented this technique still it crashing issue has not gone away. I have to disable my culling code for now. – Benny Khoo Apr 11 '14 at 05:37
  • @BennyKhoo, could you share your implementation? To me it is still very unclear what self.aShapeNode is supposed to be in the subclassed SKShapeNode... – zeeple Apr 12 '14 at 03:22
  • @user1639164 I have a derived class of SKNode. Then I copy-and-pasted the fragment exactly as above overriding the removeFromParent method. In my context, the self.aShapeNode was initialised to an instance of SKShapeNode which I used it to draw a shape for visual purpose in an unfinished game. It still crashed randomly. My temp solution was to disable my culling algorithm, avoiding the call to removeFromParent. As it turned out, SpriteKit seem to be able to maintain a stable number of nodes at a time. I would be interested to know if there is any post that show how SK auto culling works. – Benny Khoo Apr 12 '14 at 04:21
2

You can call this method before you dealloc (or present new scene) the SKShapeNodes

- (void)cleanUpChildrenAndRemove:(SKNode*)node {
    for (SKNode *child in node.children) {
        [self cleanUpChildrenAndRemove:child];
    }
    [node removeFromParent];
}

I had the same problem but that solved it. My original question:

SKShapeNode producing crash sometimes on dealloc EXC_BAD_ACCESS

Community
  • 1
  • 1
Draelach
  • 531
  • 3
  • 14
1

I also crash with SKShapeNode when its parent remove from superview in iOS 7.1

My work around is set SKShapeNode property to nil before it remove from parent

saranpol
  • 2,177
  • 1
  • 23
  • 22
  • Do you remove your SKShapeNode from parent first? – Alexander Bekert Mar 15 '14 at 22:51
  • Nope just set it to nil before its parent call removeFromParent ex. -(void)removeFromParent { self.myShapeNode = nil; [super removeFromParent]; } anyways this is apple bug and this is just work around. – saranpol Mar 16 '14 at 04:39
  • @saranpol I don't get what self.myShapeNode would be if this is inside of a SKShapeNode subclass? – Nic Hubbard Mar 27 '14 at 04:20
  • In my case, self is SKSpriteNode subclass and I have myShapeNode (SKShapeNode) as property and child. The point is we just need to dealloc (set nil) SKShapeNode before it automatic remove from parent when it's parent remove from scene. – saranpol Mar 27 '14 at 08:30
1

Not sure if my situation exactly mimics yours, but I was getting the same error (with the same stack trace) and realized that I had set up two classes that were each keeping the SKShapeNode object as properties. I'm pretty sure that when I called removeFromParent to remove object node in ClassA the object was deallocated. Then, in ClassB, I called self.node = aNewNode (keep in mind that the object that self.node pointed to had been deallocated), the auto-synthesized setter tried to de-allocate node for a second time.

I thought ARC was supposed to keep track of all this, but the bug is very sporadic so I'm honestly not 100% sure what's going on. I have an SKSpriteNode with the same pattern and I have never seen it causing this error. My fix right now has been to make the ClassB property weak, so it's not a problem if self.node has already been deallocated. Hope that helps!

rcarlson
  • 21
  • 1
  • This helped me. I was able to fix my problem. My SKShapeNode had a __weak reference to it in another class. I removed that __weak property and the crashing stopped. – shad Apr 29 '14 at 18:18
1

I am also experiencing the crash. However, the solutions to override the removeFromParent method and setting the SKShapeNode to nil didn't work for me.

I did discover that my crash was the combination of:

  1. Having a SKShapeNode (either a subclass OR a child of a SKNode)

  2. Having a SKPhysicsNode attached to a node (attached to a parent SKNode OR the SKShapeNode).

  3. Calling the method removeFromParent

The solution I ended up using was overriding the removeFromParent method in my SKShapeNode subclass with the following:

- (void)removeFromParent
{
    if( self.physicsBody != nil )
    {
        self.physicsBody = nil;
    }

    [super removeFromParent];
}