我正在使用 SpriteKit 编写一个 iOS 应用程序,但我发现有关从其父级中删除
SKNode
的信息存在冲突,这使我陷入了僵局。
数据竞争在 SpriteKit 文档中专门解决了:
如果您在 SpriteKit 框架深处遇到分段错误或其他类型的崩溃,则您的代码很可能正在场景委托回调之外修改 SpriteKit 对象。
所以我知道我应该只在这些回调中修改(删除)节点。事实上,当使用删除主队列上的节点的完成块运行
SKAction
时,我可靠地经历了数据竞争/崩溃/异常。甚至 SKAction.removeFromParent
操作本身也会引发异常并变得基本上无法使用。
在 SKNode.removeFromParent()
更新回调中调用
SKScene
会引发异常并记录一条控制台消息,表明仅允许在主线程中删除节点。这与文档相矛盾,所以我闻到了 Apple 一些被遗忘的代码。这与
SpriteKit 文档的另一部分冲突:
SKViewDelegate、SKSceneDelegate 和 SKScene 的所有 SpriteKit 回调都发生在主线程中,因此,这些是访问或操作节点的安全位置。但是,我可以通过在重写的
SKScene
方法中放置一个断点来证明
SKScene.update(:)
回调不在主线程中被调用。它们在某些 SceneKit 渲染串行队列中被调用。在我的应用程序中,我使用
SCNSceneRenderer.overlaySKScene
和
SKScene
来补充
SCNScene
。在 SceneKit 中,事情可能会有所不同,但它仍然是一个 SpriteKit 场景。那么现在是什么情况?我
无法删除更新回调中的节点,因为它没有在主队列上调用。我也无法删除主队列中的节点,因为它会导致数据竞争。
解决方案#1是根本不删除任何节点,但这并不是真正的解决方案。有些节点,例如SKShapeNode
,有一个固定的框架,只能在初始化时设置。我可以将它们移动到隐藏的“墓地”节点或缓存节点,但这只是一些开箱即用的解决方法。
解决方案#2是通过暂停SCNView
来防止数据竞争,然后等到最后一帧完成渲染,然后才删除主线程中的节点,然后再次取消暂停视图。这很麻烦,而且一开始就没有必要。它要求我找到一些静止帧(黑色交叉淡入淡出过渡?),我可以在其中暂停视图而不打扰用户。有人遇到过这样的麻烦并找到其他方法吗?
我有一种感觉,Apple 在 iOS 8 中实现了
SCNSceneRenderer.overlaySKScene
,然后在未来几年进行了其他更改,然后就忘记了它。