Swift 6中预加载SKTextureAtlas时崩溃问题求助
Swift 6下SKTextureAtlas.preload在初始化器中调用崩溃的解决方案
问题重现
以下代码在Swift 6模式下运行会崩溃,只需向项目中添加名为foo.png的图片即可触发:
import UIKit import SpriteKit class ViewController: UIViewController { let textureAtlas: SKTextureAtlas required init?(coder: NSCoder) { textureAtlas = SKTextureAtlas(dictionary: ["Foo": UIImage(named: "foo.png")!]) super.init(coder: coder) textureAtlas.preload { } } }
崩溃栈核心信息指向_dispatch_assert_queue_fail和swift_task_isCurrentExecutorImpl,说明是Swift 6的并发检查机制触发的断言失败。
崩溃原因
Swift 6默认开启严格并发模式(Strict Concurrency Checking),对实例生命周期的并发访问做了更严格的校验:
- 在
init方法中调用preload时,闭包会隐式捕获self - 此时
self虽已调用super.init,但整个实例初始化流程尚未完全完成 preload的闭包会在后台队列com.apple.spritekit.preloadQueue执行,Swift并发运行时检测到对未完全初始化实例的跨队列访问,触发断言崩溃
Swift 5下即使开启严格并发检查,对该场景的校验逻辑未达到Swift 6的强度,因此不会触发崩溃。
解决方案
方案1:将preload调用移至viewDidLoad(推荐)
viewDidLoad执行时,实例已完全初始化,此时调用preload不会触发并发断言:
import UIKit import SpriteKit class ViewController: UIViewController { let textureAtlas: SKTextureAtlas required init?(coder: NSCoder) { textureAtlas = SKTextureAtlas(dictionary: ["Foo": UIImage(named: "foo.png")!]) super.init(coder: coder) } override func viewDidLoad() { super.viewDidLoad() textureAtlas.preload { // 预加载完成后的逻辑写在这里 } } }
方案2:显式避免闭包捕获self(仅特殊场景使用)
如果必须在初始化阶段启动预加载,可以通过显式声明避免闭包捕获self(但需确保闭包内不访问实例属性/方法):
required init?(coder: NSCoder) { let atlas = SKTextureAtlas(dictionary: ["Foo": UIImage(named: "foo.png")!]) textureAtlas = atlas super.init(coder: coder) atlas.preload { // 闭包内不访问self的任何内容 } }
此方案仅适用于闭包无需访问实例成员的场景,多数情况下推荐使用方案1。
内容的提问来源于stack exchange,提问作者OMGPOP
相关产品推荐
相关产品推荐

