Pytanie Sprite Kit iOS 7.1 crash na removeFromParent


Zaktualizowałem iPada Air do wersji 7.1 i Xcode do wersji 5.1. Xcode chciał zaktualizować mój projekt do zalecanych ustawień, zgodziłem się.

Potem moja gra zaczęła się rozbijać w kilku miejscach, kiedy usunę węzeł z rodzica.

To była dla mnie niespodzianka - przed aktualizacją nie było skrzyżowań. Przywróciłem mój projekt i dowiedziałem się, co robi Xcode - zmienia tylko ciąg Architektury:

Przed: Architektury przed aktualizacją http://i57.tinypic.com/68v4nr.png

Po: Architektury po aktualizacji http://i61.tinypic.com/vpeo2a.png

W starej wersji nie ma awarii. Nie ma awarii, jeśli usunę wsparcie dla arm64 w nowej wersji. W symulatorze nie ma awarii zarówno w nowych, jak i starszych wersjach.

Gdzie powinienem zwrócić uwagę w moim kodzie?

Stos kodów:

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

AKTUALIZACJA: Nowe informacje:

Mój błąd:

Błąd http://i58.tinypic.com/30iizih.png

Stos:

Stos połączeń http://i61.tinypic.com/e6vswj.png

Dowiedziałem się czegoś złego. Mój węzeł, który chcę usunąć, składa się z dwóch SKSpriteNodes (mój obraz ikonki i jego cień) i jednego SKShapeNode (cienka czarna lina). Próbowałem zastąpić moją linę SKShapeNode liną SKSpriteNode i odniosłem sukces - problem minął.

Kod do usunięcia mojego węzła z nadrzędnego:

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

Kod, aby dodać linę SKShapeNode do mojego węzła:

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

Kod do dodania liny SKSpriteNode do mojego węzła, która rozwiązuje 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];
}

Wygląda na to, że coś jest nie tak w moim kodzie dodającym SKShapeNode, ale nie mogę zrozumieć, co to jest.


11
2018-03-14 08:01


pochodzenie


czy możesz opublikować stos wywołań + kod / metodę, która inicjuje usunięcie z rodzica? Wyślij również aktualny komunikat o błędzie. Dodaj punkt przerwania wyjątku, jeśli jeszcze tego nie zrobiłeś. - LearnCocos2D
Już powiedziałeś z tytułem, że gra się zawiesza na removeFromParent. Co jeszcze chcesz wiedzieć? - El Tomato
FWIW Otrzymuję podobną / tę samą awarię, usuwając węzeł z węzłem potomnym SKShapeNode. Zdarza się to zawsze, gdy włączam debugowanie, które dodaje SKShapeNode do każdego węzła. To wygląda jak błąd. - LearnCocos2D
Przez kilka nocy zmagałem się, próbując zrozumieć, co się stało i dlaczego otrzymuję EXC_BAD_ACCESS, próbowałem wszystkiego i odwierciłem do SKCSprite :: removeSubsprite - teraz po zobaczeniu tego posta, przynajmniej będę się dobrze przespać tej nocy - vim
To może być błąd w Sprite Kit. Czy złożyłeś to w Apple? - rickster


Odpowiedzi:


Również zawieszam się z SKShapeNode, gdy jego rodzic usunie z superview w iOS 7.1

Moja praca jest ustawiona na właściwość SKShapeNode na zero przed usunięciem z obiektu nadrzędnego


1
2018-03-15 05:19



Czy najpierw usuwasz SKShapeNode z rodzica? - Alexander Bekert
Nie wystarczy ustawić go na zero przed wywołaniem nadrzędnym removeFromParent ex. - (void) removeFromParent {self.myShapeNode = nil; [super removeFromParent]; } tak czy inaczej to jest błąd Apple i to jest po prostu obejście. - saranpol
@saranpol Nie rozumiem, co byłoby self.myShapeNode, jeśli jest to wewnątrz podklasy SKShapeNode? - Nic Hubbard
W moim przypadku self jest podklasą SKSpriteNode i mam myShapeNode (SKShapeNode) jako własność i dziecko. Chodzi o to, że musimy po prostu dealloc (ustawić zero) SKShapeNode przed automatycznym usunięciem z rodzica, gdy jest to rodzic usuwany ze sceny. - saranpol


Wydaje się, że dzieje się tak tylko w systemie iOS 7.1. Nie jestem pewien, czy to błąd Apple'a, ale wydaje się, że to naprawia:

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

    [super removeFromParent];
}

4
2018-03-21 01:35



Tak! Ten kod powinien zadziałać. Ale potrzebujesz podklasy SKNode, aby zastąpić removeFromParent. - Alexander Bekert
Czy robisz to w podklasie SKNode? Nie dostaję tego, co self.aShapeNode będzie w podklasie. - Nic Hubbard
W jaki sposób działa to z subklassed ShapeNode? Dla mnie byłaby to: - zeeple
- (void) removeFromParent {[self removeFromParent]; self = zero; [super removeFromParent]; } - zeeple
Wdrożono tę technikę, jednak problem ten nie zniknął. Na razie muszę wyłączyć mój kod uboju. - Benny Khoo


Możesz wywołać tę metodę zanim dealloc (lub zaprezentujesz nową scenę) SKShapeNodes

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

Miałem ten sam problem, ale to rozwiązało. Moje oryginalne pytanie:

Wywołanie awarii SKShapeNode czasami na dealloc EXC_BAD_ACCESS


2
2017-08-21 02:11





Nie jestem pewien, czy moja sytuacja dokładnie naśladuje twoją, ale otrzymałem ten sam błąd (z tym samym śladem stosu) i zdałem sobie sprawę, że założyłem dwie klasy, z których każda utrzymywała SKShapeNode obiekt jako właściwości. Jestem prawie pewien, że kiedy zadzwoniłem removeFromParent aby usunąć obiekt node w ClassA obiekt został zwolniony. Następnie w ClassB, Dzwoniłem self.node = aNewNode (należy pamiętać, że obiekt, który self.node wskazał, że został zwolniony), auto-syntezator ustawił próbę de-alokacji node po raz drugi.

Myślałem, że ARC ma to wszystko śledzić, ale błąd jest bardzo sporadyczny, więc nie jestem w 100% pewny, co się dzieje. mam SKSpriteNode z tym samym wzorem i nigdy nie widziałem powodującego tego błędu. Moja poprawka w tej chwili polegała na stworzeniu ClassB własność weak, więc nie jest to problemem, jeśli self.nodezostał już zwolniony. Mam nadzieję, że pomaga!


1
2018-03-20 15:59



To mi pomogło. Udało mi się naprawić mój problem. Mój SKShapeNode miał odniesienie __weak do niego w innej klasie. Usunąłem tę właściwość __weak, a awarie ustały. - shadanan


Mam również awarię. Jednak rozwiązania, które zastąpiły metodę removeFromParent i ustawienie SKShapeNode na zero, nie działały dla mnie.

Odkryłem, że moja katastrofa była połączeniem:

  1. Posiadające SKShapeNode (albo podklasę LUB dziecko z SKNode)

  2. Posiadanie węzła SKPhysicsNode dołączonego do węzła (dołączonego do nadrzędnego SKNode OR SKShapeNode).

  3. Wywołanie metody removeFromParent

Rozwiązaniem, z którego skorzystałem, było przesłonięcie metody removeFromParent w mojej podklasie SKShapeNode za pomocą:

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

    [super removeFromParent];
}

1
2017-07-29 02:43