Caching 添加、删除和缓存节点

Caching 添加、删除和缓存节点,caching,sprite-kit,exc-bad-access,sknode,Caching,Sprite Kit,Exc Bad Access,Sknode,背景信息: 我正在创建一个具有多个“楼层”的SpriteKit 2D平台风格的游戏。当用户进入一个“入口”时,他被转移到另一个楼层(一层向上或一层向下)。如果用户在与最近的繁殖点不同的楼层死亡,他将与繁殖点一起返回该楼层 问题:升级到iOS8后,此场景会导致游戏崩溃,出现EXC\u BAD\u访问异常/错误。即使在阅读了关于如何调试此类错误的多个教程之后,我似乎也找不到问题所在 因此,如果有人能看看我的代码,告诉我他们是否能找到更好的方法来做我正在做的事情,最好不要让游戏冻结,我会非常感激 游戏

背景信息: 我正在创建一个具有多个“楼层”的SpriteKit 2D平台风格的游戏。当用户进入一个“入口”时,他被转移到另一个楼层(一层向上或一层向下)。如果用户在与最近的繁殖点不同的楼层死亡,他将与繁殖点一起返回该楼层

问题:升级到iOS8后,此场景会导致游戏崩溃,出现
EXC\u BAD\u访问异常/错误。即使在阅读了关于如何调试此类错误的多个教程之后,我似乎也找不到问题所在

因此,如果有人能看看我的代码,告诉我他们是否能找到更好的方法来做我正在做的事情,最好不要让游戏冻结,我会非常感激

游戏布局: 我有一个自定义类
TileMapLayer
,它基于Ray Wenderlich的书《iOS游戏教程》中的同一个类。它基本上是一个自定义的SKNode类,包含多个32x32p的tile,组合起来为我的游戏创建背景

我在游戏开始时加载相应的楼层,然后在用户上/下时调用加载另一楼层。在这种情况下,我首先将当前楼层缓存在
NSDictionary
中,以便在用户返回该楼层时可以访问它

// Cache the current floor
[_allFloors setObject:_bgLayer forKey:[NSString stringWithFormat:@"floor_%tu", (unsigned)_currentFloor]];

// Remove the current floor
[_bgLayer removeFromParent];

// Get the cached (next) floor if it exists, if not create it
TileMapLayer *cachedFloor = [_allFloors objectForKey:[NSString stringWithFormat:@"floor_%tu", (unsigned)(_currentFloor + 1)]];
if (!cachedFloor) {
    _bgLayer = [self createScenery:[NSNumber numberWithInteger:(_currentFloor + 1)]];
    NSLog(@"creating new floor");
} else {
    _bgLayer = cachedFloor;
    NSLog(@"getting cached floor");
}

// Display the new floor
[_worldNode addChild:_bgLayer];

// Increment the floor number
_currentFloor++;
(我也有类似的下楼方法)。 无论是在升级到iOS8之前还是之后,这都非常有效

当用户死亡时,他将被传送回最后一个繁殖点。如果最后一个点位于不同的楼层,则该楼层也会相应更改。 我将球上的此自定义动作称为动画的一部分:

SKAction *changeFloor = [SKAction runBlock:^{
  if (self.spawnFloor != _currentFloor) { 
    [_bgLayer removeFromParent];
    _bgLayer = [_allFloors objectForKey:[NSString stringWithFormat:@"floor_%tu", (unsigned)self.spawnFloor]];
    [_worldNode addChild:_bgLayer];
    _currentFloor = self.spawnFloor;
    NSLog(@"Made it here");
  }
}];
正如你所看到的,没有太大区别。在这里制作的
将被记录到控制台,但游戏随即冻结。我调用的下一个方法(将球移动到正确的位置)根本不会执行

只是为了好玩,我试着在将
\u bgLayer
从其父对象中删除之前缓存它,如下所示:

if (self.spawnFloor != _currentFloor) {
  [_allFloors setObject:_bgLayer forKey:[NSString stringWithFormat:@"floor_%tu", (unsigned)_currentFloor]];
  ...
由于某种原因,当我这么做的时候,游戏并没有冻结。但是,地板最终混合在一起,就好像从未从其父层移除过
\bgLayer
(注意:“旧”瓷砖不再对任何物理模拟产生反应,它们只是在背景中无所作为)

在从父级移除后,我还尝试了
[\u bgLayer removeAllChildren]
(移除单个磁贴):

[_bgLayer removeFromParent];
[_bgLayer removeAllChildren];
但这会导致当我在重生后回到这层楼时,_bgLayer是空的。好像我在将其存储到字典之前删除了所有节点。我的猜测是,字典只引用了bgLayer的位置,而不是内容本身。所以,当我删除屏幕上的所有子对象时,我也有效地删除了缓存中的所有子对象

Wrapup: 我知道这是一个很长的问题,如果你能做到这一点,我想说声谢谢。如果您有任何问题,请在下面的评论中毫不犹豫地提问。 最终,我想知道的是:如何解决我的问题,使游戏不会冻结?缓存楼层而不引起内存问题的最佳做法是什么?删除和添加屏幕上的节点,并始终将当前楼层(屏幕上的楼层)称为
\bgLayer
,最佳做法是什么


再次感谢你

iOS8在删除块内节点时的行为似乎有所不同,就好像您试图在后台线程上执行此操作一样,因此有时会导致奇怪的崩溃。这可能是一个bug,但在此之前,我建议两件事:

1.-在块内标记要删除的节点,但在
更新:
循环中进行标记。你不会注意到任何不同

2.-确保removeFromParent发生在主线程中。但不确定这是否能解决问题

__strong typeof(_bgLayer) strongLayer = _bgLayer;
dispatch_async(dispatch_get_main_queue(), ^{
        [strongLayer removeFromParent];
    });

您是否已尝试添加异常断点?当崩溃发生时,Xcode突出显示哪一行?调用堆栈是什么?日志中打印了什么,任何类型的错误消息?我尝试添加一个异常断点,但xcode没有突出显示除@autoreleasepool之外的任何行。控制台中也没有打印任何内容…:(您好,谢谢!这个(标记为删除并从更新循环执行)昨晚确实出现在我的脑海中,所以我尝试了它,它成功了。它比以前的工作多了一点,所以我希望他们能修复它,但现在还可以。非常感谢!)