NSGenericException原因集合<NSConcreteMapTable:xxx>。

7
这是我在呈现SKScene时看到的错误,这个错误会随机发生,且无法复制。 * 由于抛出了未捕获的异常'NSGenericException',原因:'在枚举时改变了Collection < NSConcreteMapTable: 0x1459da60 > '。
发生了什么?
如果需要其他信息,请告诉我。
谢谢。 编辑:
*** First throw call stack:
(
    0   CoreFoundation                      0x025601e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x022298e5 objc_exception_throw + 44
    2   CoreFoundation                      0x025efcf5 __NSFastEnumerationMutationHandler + 165
    3   Foundation                          0x01e47f03 -[NSConcreteMapTable countByEnumeratingWithState:objects:count:] + 66
    4   CoreFoundation                      0x0253d77f -[__NSFastEnumerationEnumerator nextObject] + 143
    5   SpriteKit                           0x01d009f2 +[SKTextureAtlas(Internal) findTextureNamed:] + 232
    6   SpriteKit                           0x01cf709c __26-[SKTexture loadImageData]_block_invoke + 1982
    7   SpriteKit                           0x01d34d09 _Z14SKSpinLockSyncPiU13block_pointerFvvE + 40
    8   SpriteKit                           0x01cf6898 -[SKTexture loadImageData] + 228
    9   SpriteKit                           0x01cf65d9 __51+[SKTexture preloadTextures:withCompletionHandler:]_block_invoke + 241
    10  libdispatch.dylib                   0x02b117b8 _dispatch_call_block_and_release + 15
    11  libdispatch.dylib                   0x02b264d0 _dispatch_client_callout + 14
    12  libdispatch.dylib                   0x02b14eb7 _dispatch_root_queue_drain + 291
    13  libdispatch.dylib                   0x02b15127 _dispatch_worker_thread2 + 39
    14  libsystem_c.dylib                   0x02de1e72 _pthread_wqthread + 441
    15  libsystem_c.dylib                   0x02dc9daa start_wqthread + 30
)
libc++abi.dylib: terminating with uncaught exception of type NSException
4个回答

4
我偶尔也会遇到同样的异常。这个问题已经存在一段时间了,我已经试图花费几周的时间来确定它的原因。
我的怀疑是由于预加载纹理,无论是手动还是由Sprite Kit自动触发,同时其他代码导致纹理被加载或访问而可能发生。
我将preloadTextures:调用减少到一个单独的调用,但我仍然会遇到此问题,只是频率较低。每当我运行从完成块或其他在不同线程上运行的代码中访问或加载图像(或可能在内部)的选择器时,我都尝试使用performSelector:onMainThread:。
今天整天我没有遇到这个崩溃,因为我将用户界面代码移到了主线程(它是从完成处理程序中调用的)。但我不能100%确定这是否修复了它。
希望这能对你有所帮助。肯定有些棘手的问题,如果你在lldb的命令窗口中执行po 0x1459da60(使用异常提供的地址),你会看到正在修改SKTextureAtlas纹理列表。希望这能帮助你确定问题来自哪里。

很棒的答案!非常感谢,那么您建议在initWithSize中调用用户界面代码使用:[self performSelectorOnMainThread...]是吗?而不是简单地使用[self aMethod]。 - Ilario
这更像是一种观察和直觉,但通常情况下,如果你怀疑某些代码正在与纹理加载并行运行(这发生在后台),那么尝试在主线程上运行特定的代码或在纹理预加载完成处理程序被调用后运行它可能是值得的。由于问题如此零散,我仍然不确定我的问题是否已经解决,或者到底是什么解决了它。 - CodeSmile
我已经在后台加载了纹理,其余的在主线程上进行,但是仍然没有改变 :( - Ilario
现在我正在尝试从initWithSize仅加载纹理,加载纹理后我调用加载UI的方法...但仍然不知道它是否会起作用。 - Ilario
1
除了我成功解决了错误之外,我不能说太多。问题的一部分是将代码堆叠在completionHandlers或其他回调块中,因为它们可能在单独的线程上运行。同时,拥有一个单独的加载场景可以确保在预加载纹理时没有其他代码正在运行,这也可能会有所帮助。 - CodeSmile

4
据我所知,这是一个与Sprite Kit方法相关的bug。
preloadTextures: withCompletionHandler:

我解决这个问题的唯一方法是完全删除这个方法。 根据苹果文档,如果您访问 size 属性,纹理也会被加载。 因此,我的解决方法就是做到这一点:

for (SKTexture *texture in self.texturesArray) {
    texture.size;
}

虽然不美观,但它却能工作!


这个解决方案是否至少解决了你的问题?或者你最终找到了另一个解决方案吗? - Zalykr
这对我也起作用了。我只是使用了 NSLog(@"%f", texture.size.width); 来避免未使用变量的警告。 - Reinaldo

2

我曾经遇到过同样的问题,当我尝试预加载两个简单的动画时。我试图在一个字典中预加载这些动画,并通过字符串键准备好调用它们。以下是我尝试的方法:

-(void)setupAnimDict {
    animDict = [[NSMutableDictionary alloc] init];

    [animDict setObject:[self animForName:@"blaze" frames:4] forKey:@"blaze"];
    [animDict setObject:[self animForName:@"flame" frames:4] forKey:@"flame"];
}

-(SKAction *)animForName:(NSString *)name frames:(int)frames {
    NSArray *animationFrames = [self setupAnimationFrames:name base:name num:frames];
    SKAction *animationAction = [SKAction animateWithTextures:animationFrames     timePerFrame:0.10 resize:YES restore:NO];
    return [SKAction repeatActionForever:animationAction];
}

-(NSArray *)setupAnimationFrames:(NSString *)atlasName base:(NSString *)baseFileName num:(int)numberOfFrames {

    [self preload:baseFileName num:numberOfFrames];

    NSMutableArray *frames = [NSMutableArray arrayWithCapacity:numberOfFrames];
    SKTextureAtlas *atlas = [SKTextureAtlas atlasNamed:atlasName];
    for (int i = 0; i < numberOfFrames; i++) {
        NSString *fileName = [NSString stringWithFormat:@"%@%01d.png", baseFileName, i];
        [frames addObject:[atlas textureNamed:fileName]];
    }

    return frames;
}

-(void)preload:(NSString *)baseFileName num:(int)numberOfFrames {

    NSMutableArray *frames = [NSMutableArray arrayWithCapacity:numberOfFrames];

    for (int i = 0; i < numberOfFrames; i++) {
        NSString *fileName = [NSString stringWithFormat:@"%@%01d.png", baseFileName, i];
        [frames addObject:[SKTexture textureWithImageNamed:fileName]];
    }

    [SKTexture preloadTextures:frames withCompletionHandler:^(void){}];
}

当我调用setupDict方法时,有时会出现与您相同的错误。问题在于我的两个动画的预加载相互干扰。通过更改preloading,我摆脱了这个错误。
[SKTexture preloadTextures:frames withCompletionHandler:^(void){}];

为了

if ([baseFileName isEqualToString:@"blaze"]) {
        [SKTexture preloadTextures:frames withCompletionHandler:^{
            [self setupFlame];
        }];
    } else {
        [SKTexture preloadTextures:frames withCompletionHandler:^(void){}];
    }

所以我希望在尝试预加载其他内容之前,首先完成了第一次预加载。

我不确定这是否是你遇到的问题,如果是,请让我们知道。


感谢您的回答,我正在尝试在initWithSize中加载SKTextureAtlas,并且所有纹理都来自这个纹理数组,而不需要预加载任何内容... 这样做可以正常工作,没有崩溃,所有精灵都能正常显示... 但我不确定这是否是正确的方法。 - Ilario

1

我在Xcode 6.3 beta / Swift 1.2中仍然遇到同样的问题。这里是一个临时解决方案,对我很有效。

SKTextureAtlas.preloadTextureAtlases([SKTextureAtlas(named: "testAtlas")], withCompletionHandler: {
    dispatch_async(dispatch_get_main_queue(), {
        handler()
    })
})

我实际上将这个函数包装起来,以便所有的预加载都通过它进行路由。这样,如果SpriteKit方面得到修复,或者这种方法存在重大缺陷,我可以删除调度。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接