You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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),对实例生命周期的并发访问做了更严格的校验:

  1. 在init方法中调用preload时,闭包会隐式捕获self
  2. 此时self虽已调用super.init,但整个实例初始化流程尚未完全完成
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 04:25:56