关于Objective C:SKShapeNode有时会在dealloc EXC_BAD_ACCESS上产生崩溃

SKShapeNode producing crash sometimes on dealloc EXC_BAD_ACCESS

在我的主要场景中,我使用以下方法创建4面墙:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-(void)createFirstWalls{

    CGFloat maxY = CGRectGetMaxY(self.frame);
    Wall* wall1=[Wall wallWithRect:self.frame color:[self randomColor] position:maxY andSpeed:speed];
    Wall* wall2=[Wall wallWithRect:self.frame color:[self randomColor] position:maxY+distance andSpeed:speed];
    Wall* wall3=[Wall wallWithRect:self.frame color:[self randomColor] position:maxY+distance*2 andSpeed:speed];
    Wall* wall4=[Wall wallWithRect:self.frame color:[self randomColor] position:maxY+distance*3 andSpeed:speed];

    wall1.name=@"1";
    wall2.name=@"2";
    wall3.name=@"3";
    wall4.name=@"4";

    [self addChild:wall1];
    [self addChild:wall2];
    [self addChild:wall3];
    [self addChild:wall4];
}

输掉比赛后,我会用经典方法呈现另一个场景

1
2
3
SKScene* gameOver =[GameOver sceneWithSize:self.view.bounds.size];
gameOver.scaleMode = SKSceneScaleModeAspectFill;
[self.view presentScene:gameOver];

问题在于,当我展示新场景时,ARC会释放我创建的所有墙,有时(或多或少有40%的机会)它会使执行崩溃。

这是我自定义的Wall类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#import"Wall.h"
static const uint32_t triangleCategory     =  0x1 << 0;
static const uint32_t wallCategory     =  0x1 << 1;


@implementation Wall

+(id)wallWithRect:(CGRect)rect color:(UIColor*)color position:(CGFloat)point andSpeed:(CGFloat)speed{
    return [[Wall alloc] initWithRect:rect color:color position:point andSpeed:speed];
}

-(id)initWithRect:(CGRect)rect color:(UIColor*)color position:(CGFloat)point andSpeed:(CGFloat)speed{
    if (self=[super init]) {
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathMoveToPoint(path, nil, CGRectGetMinX(rect), 100);
        CGPathAddLineToPoint(path, nil, CGRectGetMinX(rect), 115);
        CGPathAddLineToPoint(path, nil, CGRectGetMaxX(rect), 115);
        CGPathAddLineToPoint(path, nil, CGRectGetMaxX(rect), 100);
        CGPathAddLineToPoint(path, nil, CGRectGetMinX(rect), 100);

        self.position=CGPointMake(0, point);
        self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path];
        self.physicsBody.linearDamping=0;
        self.physicsBody.affectedByGravity=NO;
        self.physicsBody.velocity=CGVectorMake(0,speed);
        self.physicsBody.collisionBitMask=0x0;
        self.physicsBody.contactTestBitMask=triangleCategory;
        self.physicsBody.categoryBitMask= wallCategory;





        self.path=path;

        CGPathRelease(path);
        path = nil;
        self.fillColor=color;
        self.lineWidth=2;
        self.strokeColor=[UIColor blackColor];
    }
    return self;
}

-(void)dealloc{
    NSLog(@"Wall: %@",self.name);
}

@end

我创建了一个dealloc方法来查看会发生什么。
如果一切正常,则输出如下:

1
2
3
4
2014-08-19 15:56:42.252 running[36425:60b] Wall: 4
2014-08-19 15:56:42.256 running[36425:60b] Wall: 3
2014-08-19 15:56:42.257 running[36425:60b] Wall: 2
2014-08-19 15:56:42.260 running[36425:60b] Wall: 1

但是当崩溃时,输出是这样的:

1
2
2014-08-19 15:57:37.767 running[36425:60b] Wall: 4
(lldb)

crash

enter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
SpriteKit`SKCSprite::removeSubsprite(SKCSprite*):
0x331751a4:  push   {r4, r5, r7, lr}
0x331751a6:  add    r7, sp, #0x8
0x331751a8:  sub    sp, #0x4
0x331751aa:  mov    r4, r0
0x331751ac:  add.w  r0, r4, #0x1bc
0x331751b0:  mov    r5, sp
0x331751b2:  str    r1, [sp]
0x331751b4:  mov    r1, r5
0x331751b6:  bl     0x33179b8c                ; unsigned long std::__1::__tree<SKCSprite*, std::__1::less<SKCSprite*>, std::__1::allocator<SKCSprite*> >::__erase_unique<SKCSprite*>(SKCSprite* const&)
0x331751ba:  add.w  r0, r4, #0x190
0x331751be:  mov    r1, r5
0x331751c0:  bl     0x33179760                ; std::__1::list<SKCSprite*, std::__1::allocator<SKCSprite*> >::remove(SKCSprite* const&)
0x331751c4:  ldr    r5, [sp]
0x331751c6:  ldrb   r0, [r5, #0xa]
0x331751c8:  ldrh   r1, [r5, #0x8]
0x331751ca:  orr.w  r0, r1, r0, lsl #16
0x331751ce:  tst.w  r0, #0x2
0x331751d2:  bne    0x331751e4                ; SKCSprite::removeSubsprite(SKCSprite*) + 64
0x331751d4:  ldr.w  r1, [r5, #412]
0x331751d8:  ldr    r1, [r1, #0x8]
0x331751da:  cmp    r1, #0x0
0x331751dc:  it     eq
0x331751de:  tsteq.w r0, #0x100
0x331751e2:  beq    0x331751ec                ; SKCSprite::removeSubsprite(SKCSprite*) + 72
0x331751e4:  mov    r0, r4
0x331751e6:  mov    r1, r5
0x331751e8:  bl     0x33179798                ; SKCSprite::removeFromOffscreenList(SKCSprite*)
0x331751ec:  movs   r0, #0x0
0x331751ee:  str.w  r0, [r5, #332]
0x331751f2:  ldr    r0, [r5]
0x331751f4:  ldr    r1, [r0, #0x28]
0x331751f6:  mov    r0, r5
0x331751f8:  blx    r1
0x331751fa:  ldrh   r0, [r4, #0xc]
0x331751fc:  orr    r1, r0, #0x40
0x33175200:  strh   r1, [r4, #0xc]
0x33175202:  ldr.w  r0, [r4, #332]
0x33175206:  cbz    r0, 0x3317522c            ; SKCSprite::removeSubsprite(SKCSprite*) + 136
0x33175208:  add.w  r1, r4, #0x14c
0x3317520c:  ldrh   r2, [r0, #0xc]
0x3317520e:  orr    r2, r2, #0x40
0x33175212:  strh   r2, [r0, #0xc] Thread 1: EXC_BAD_ACCESS(code=2, address=0x302e373d)
0x33175214:  ldr    r1, [r1]
0x33175216:  ldrh   r0, [r1, #0xc]
0x33175218:  orr    r0, r0, #0x40
0x3317521c:  strh   r0, [r1, #0xc]
0x3317521e:  ldr.w  r0, [r1, #332]
0x33175222:  add.w  r1, r1, #0x14c
0x33175226:  cmp    r0, #0x0
0x33175228:  bne    0x3317520c                ; SKCSprite::removeSubsprite(SKCSprite*) + 104
0x3317522a:  ldrh   r1, [r4, #0xc]
0x3317522c:  orr    r0, r1, #0x2
0x33175230:  strh   r0, [r4, #0xc]
0x33175232:  b      0x33175236                ; SKCSprite::removeSubsprite(SKCSprite*) + 146
0x33175234:  ldrh   r0, [r4, #0xc]
0x33175236:  tst.w  r0, #0x80
0x3317523a:  bne    0x3317524a                ; SKCSprite::removeSubsprite(SKCSprite*) + 166
0x3317523c:  orr    r0, r0, #0x80
0x33175240:  strh   r0, [r4, #0xc]
0x33175242:  ldr.w  r4, [r4, #332]
0x33175246:  cmp    r4, #0x0
0x33175248:  bne    0x33175234                ; SKCSprite::removeSubsprite(SKCSprite*) + 144
0x3317524a:  add    sp, #0x4
0x3317524c:  pop    {r4, r5, r7, pc}
0x3317524e:  nop

我尝试了一些类似的事情,例如在解除分配之前将物理体设置为零,但是没有任何效果...任何想法?


这似乎是iOS 7.1中的SKShapeNode的错误...

但是最后我找到了答案!!!希望这可以帮助更多的人。

我实现了一种方法来删除当前节点中的所有子元素

1
2
3
4
5
6
- (void)cleanUpChildrenAndRemove:(SKNode*)node {
    for (SKNode *child in node.children) {
        [self cleanUpChildrenAndRemove:child];
    }
    [node removeFromParent];
}

然后在呈现新场景之前调用该函数

1
2
3
4
    [self cleanUpChildrenAndRemove:self]; //god bless this method
    SKScene* gameOver =[GameOver sceneWithSize:self.view.bounds.size];
    gameOver.scaleMode = SKSceneScaleModeAspectFill;
    [self.view presentScene:gameOver];

不再崩溃,也不会BAD_EXC ...是一个真正的奇迹。


一个可能的原因是您没有释放CGPathRef对象。请参阅此问题/答案。

尝试在最后使用路径添加以下两行:

1
2
3
4
self.path=path;

CGPathRelease(path);
path = nil;

过去也存在一个错误,其中删除SKShapeNodes导致崩溃。确保运行最新的Xcode 5版本/ iOS 7.1 SDK。