Sprite Kit 中的 SKSpinLockSync 是啥以及如何修复它

Posted

技术标签:

【中文标题】Sprite Kit 中的 SKSpinLockSync 是啥以及如何修复它【英文标题】:What is SKSpinLockSync in Sprite Kit and how do I fix itSprite Kit 中的 SKSpinLockSync 是什么以及如何修复它 【发布时间】:2013-10-18 12:00:54 【问题描述】:

我收到了包含以下堆栈跟踪的错误报告,但我不知道问题出在哪里。我已经看到一些建议,这可能是由于在纹理图集中使用了发射器的图像,或者在添加发射器时在同一运行循环中删除了发射器,但我认为这两种情况都不会发生。这是一个零星的问题,我无法重新创建它。我只在错误报告中看到它。我很乐意提供任何帮助。

0    libsystem_platform.dylib    OSSpinLockLock + 1
1    SpriteKit   SKSpinLockSync(int*, void ()() block_pointer) + 92
2    SpriteKit   -[SKTexture loadImageData] + 300
3    SpriteKit   -[SKTexture size] + 42
4    SpriteKit   SKCEmitterSprite::update(double) + 3136
5    SpriteKit   SKCSprite::update(double) + 354
6    SpriteKit   SKCSprite::update(double) + 354
7    SpriteKit   -[SKScene _update:] + 174
8    SpriteKit   -[SKView(Private) _update:] + 324
9    SpriteKit   -[SKView renderCallback:] + 820
10   SpriteKit   __29-[SKView setUpRenderCallback]_block_invoke + 130
11   SpriteKit   -[SKDisplayLink _callbackForNextFrame:] + 254
12   QuartzCore  CA::Display::DisplayLinkItem::dispatch() + 98
13   QuartzCore  CA::Display::DisplayLink::dispatch_items(unsigned long long, unsigned long long, unsigned long long) + 344
14   IOMobileFramebuffer     IOMobileFramebufferVsyncNotifyFunc + 104
15   IOKit   IODispatchCalloutFromCFMessage + 248
16 ...   CoreFoundation  __CFMachPortPerform + 136
17   CoreFoundation  __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 34
18   CoreFoundation  __CFRunLoopDoSource1 + 346
19   CoreFoundation  __CFRunLoopRun + 1406
20   CoreFoundation  CFRunLoopRunSpecific + 524
21   CoreFoundation  CFRunLoopRunInMode + 106
22   GraphicsServices    GSEventRunModal + 138
23   UIKit   UIApplicationMain + 1136
24   myApplication  main.m line 16  main

编辑:我现在意识到我在几种不同的情况下遇到了 SKSpinLockSync 问题,而不总是与发射器有关。我认为我经常在发射器上看到它的唯一原因是因为这是应用程序中加载图像的最大份额,所以它只是统计上最有可能的。堆栈跟踪的前四行始终相同。所以,直到并包括[SKTexture Size]

【问题讨论】:

【参考方案1】:

我遇到了同样的问题,当我打电话时发现

NSString *p = [[NSBundle mainBundle] pathForResource:name ofType:@"sks"];  
SKEmitterNode *e = [NSKeyedUnarchiver unarchiveObjectWithFile:p];  

很多时候,它会随机使应用程序崩溃并具有相同的崩溃日志

SKSpinLockSync(int*, void ()() block_pointer) + 36  
-[SKTexture loadImageData] + 252  
-[SKTexture size] + 44  
SKCEmitterSprite::update(double) + 2928  

我认为这是 Sprite Kit 的问题,希望苹果能尽快解决这个问题

我的解决方案是:不要每次都调用 unarchiveObjectWithFile

unarchiveObjectWithFile 可能与 IO 有关,如果您像游戏中的每一帧一样经常这样做,这可能会崩溃,或者问题来自 SKTexture 缓存系统在需要纹理数据并在非线程安全中调用 loadImageData 时出现问题。

所以我用副本重用 SKEmitterNode,这是我的功能

// Emitter Pool
- (SKEmitterNode*)getEmitter:(NSString*)name 
    if(!mDictEmitter)
        self.mDictEmitter = [NSMutableDictionary new];
    SKEmitterNode *e = [mDictEmitter objectForKey:name];
    if(!e)
        NSString *p = [[NSBundle mainBundle] pathForResource:name ofType:@"sks"];
        e = [NSKeyedUnarchiver unarchiveObjectWithFile:p];
        [mDictEmitter setObject:e forKey:name];
    
    return [e copy];

【讨论】:

【参考方案2】:

OSSpinLock 是一种确保多线程应用程序中的原子操作的机制。

我不认为这里存在多线程问题,而是 loadImageData 可能提供了在工作的原子部分崩溃的数据。这可能是由于包中缺少(或损坏或不受支持的文件格式)纹理。它肯定是一个发射器导致纹理加载失败。

尝试在发布配置(编辑方案)中测试您的应用一段时间。一些错误仅在启用优化的发布版本中出现。您还可以构建应用程序的临时版本并将其部署到您的设备并进行测试。您现在的首要目标应该是在您自己的设备上验证问题,否则可能很难确定。

调用堆栈确实给了您一些提示:问题在于发射器是精灵的子精灵,而精灵的子精灵是场景的子精灵的子精灵。也许这会缩小范围。

【讨论】:

这并不能解决我的问题,但它为我提供了一些关于在哪里寻找它的线索。谢谢。【参考方案3】:

我在类似的案例中遇到了同样的崩溃,并且能够解决它

就我而言,我试图通过 SKAction 回放图像序列:

[SKAction animateWithTextures: timePerFrame:]

我不确定原因是什么,但我的直觉是它与未预加载的纹理有关。该方法的文档没有提供关于是否必须预加载它们的任何见解,所以这纯粹是推测,但从我收集到的 SpriteKit 似乎令人窒息,因为它无法以足够快的速度加载图像以显示它们。

无论如何,使用[SKTexture preloadTextures:... 可以完全解决我的崩溃问题

希望它对你和其他人也有帮助!

【讨论】:

【参考方案4】:

我在使用 SpriteKit 制作游戏时遇到了类似的问题。能够从互联网搜索获得的所有帖子对解决方案都是无效的。有时,如果动作包含多线程概念,则特殊动作类型会导致此问题,而不是由于捆绑中的纹理损坏或丢失。我确实解决了它!换句话说,即使看起来没有明显的多线程,您的代码也可能间接地具有一种多线程。

【讨论】:

【参考方案5】:

我在 Sprite Kit 中的粒子遇到了这个确切的问题(相同的崩溃日志堆栈)。我在几个小时内尝试了很多东西——最后弄明白了,有点像:这似乎是因为源纹理位于 Texture Atlas 文件夹中,并且它没有相应的 @2x 图像。尝试将纹理移出纹理图集和/或添加图像的 @2x 版本。

【讨论】:

【参考方案6】:

如果使用SKEmitterNode 并同时创建多个(大约同时关闭),我的游戏仍然崩溃。我必须手动预加载(创建并添加发射器到场景),然后我能够防止崩溃,我必须让粒子效果被创建并添加到场景中并让它运行它的动作。然后我将粒子 alpha 设置为 0,以隐藏它。到那时我很失望,我预装了所有东西。

使用上面的SKEmitterNodegetEmitter 方法,当我大约在同一时间创建多个发射器时,我仍然会崩溃。我通过在开始时创建一个粒子来解决此问题,将其添加到场景中,并将其 alpha 设置为 0。这是一种笨拙的方式,它会爆炸,但它可以工作并且不会崩溃。

例子:

@implementation MyGameScene

- (instancetype)initWithSize:(CGSize)size

    if(self = [super initWithSize:size])
        HitEmitter *emitter = [HitEmitter particle]; // class method use getEmitter method
        [self addChild:emitter];
        [emitter setName:@"temp"];
        [emitter setAlpha:0];

        // add a second timer to remove it.
        [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(removeEmitter) userInfo:nil repeats:NO];
     return self;

【讨论】:

【参考方案7】:

我正在构建一个 Spritekit 应用程序。该应用程序不规则地崩溃,没有调试信息,只留下一个指向可怕的 SKSpinLockLock 的指针。该应用程序在一组复杂的“动画”动作中崩溃了。就像 R.Randazzo 在他的帖子中提到的那样,延迟图像加载可能会导致这些问题。但是我所有的动画都使用预加载的图像。最终,我发现了一个“隐藏在阴影中”的延迟加载图像,等待作为我的动画序列和用户交互的结果呈现。在预加载此图像后,SKSpinLockLock 幽灵消失了。注:我正在为 ios 7 + 开发

【讨论】:

以上是关于Sprite Kit 中的 SKSpinLockSync 是啥以及如何修复它的主要内容,如果未能解决你的问题,请参考以下文章

使用 Swift 在 sprite kit 中的多个场景中重用相同的 sprite 节点

检测 Sprite Kit 中的长触摸

Sprite Kit中的UIScrollView水平滚动菜单

游戏中的三角学——Sprite Kit 和 Swift 教程

游戏中的三角学——Sprite Kit 和 Swift 教程

Swift Sprite Kit 中的游戏中心