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

从Core Data加载SKTexture数组时性能不佳的问题排查

解决SpriteKit动画加载延迟的优化方案

核心问题分析

你当前的代码在主线程同步执行SKTexture(image: UIImage(data: ...))的转换操作,多帧场景下会阻塞UI线程,直接导致SwiftUI视图加载时出现1-2秒的延迟。

具体优化方案

1. 后台异步预加载纹理

把纹理加载逻辑放到全局后台队列执行,避免阻塞主线程。加载完成后回到主线程缓存结果,还可通过批量预加载进一步优化渲染性能:

func preloadAnimationTextures(for frames: [AnimationFrame], completion: @escaping ([SKTexture]) -> Void) {
    DispatchQueue.global(qos: .userInitiated).async {
        var textures = [SKTexture]()
        for frame in frames {
            guard let data = frame.imageData, let image = UIImage(data: data) else {
                continue // 处理错误,避免强制解包崩溃
            }
            textures.append(SKTexture(image: image))
        }
        // 批量预加载纹理,提前完成GPU层面的准备
        SKTexture.preload(textures) {
            DispatchQueue.main.async {
                completion(textures)
            }
        }
    }
}

2. 实现纹理缓存机制

用单例类缓存已加载的纹理,根据动画的唯一标识(比如Core Data实体的objectID)存储,避免重复加载:

class TextureCache {
    static let shared = TextureCache()
    private var cache = [NSManagedObjectID: [SKTexture]]()
    
    func store(textures: [SKTexture], for animationID: NSManagedObjectID) {
        cache[animationID] = textures
    }
    
    func retrieve(for animationID: NSManagedObjectID) -> [SKTexture]? {
        return cache[animationID]
    }
}

3. 预判用户操作触发预加载

在用户可能打开动画的前置场景提前触发预加载:

  • 比如用户进入动画列表页时,加载列表中前几个动画的纹理
  • 或者检测到用户滑动到某个动画条目、长按条目时,触发对应动画的纹理预加载

示例:SwiftUI列表中,条目出现时触发预加载

struct AnimationList: View {
    let animations: [Animation]
    
    var body: some View {
        List(animations) { animation in
            AnimationRow(animation: animation)
                .onAppear {
                    guard TextureCache.shared.retrieve(for: animation.objectID) == nil else { return }
                    let sortedFrames = animation.frames.sorted(by: { $0.order < $1.order })
                    preloadAnimationTextures(for: sortedFrames) { textures in
                        TextureCache.shared.store(textures: textures, for: animation.objectID)
                    }
                }
        }
    }
}

4. SwiftUI视图中使用预加载纹理

用户打开动画视图时,直接从缓存取纹理,无缓存时显示加载状态并兜底触发加载:

struct AnimationPlayerView: View {
    let animation: Animation
    
    var body: some View {
        Group {
            if let textures = TextureCache.shared.retrieve(for: animation.objectID) {
                SpriteView(scene: AnimationScene(textures: textures))
            } else {
                ProgressView("加载中...")
                    .onAppear {
                        let sortedFrames = animation.frames.sorted(by: { $0.order < $1.order })
                        preloadAnimationTextures(for: sortedFrames) { textures in
                            TextureCache.shared.store(textures: textures, for: animation.objectID)
                        }
                    }
            }
        }
    }
}

额外优化点

  • 移除强制解包:替换代码中的!,处理可选值避免崩溃
  • 内存管控:收到内存警告时,清空部分不常用的缓存内容
  • 优先级调整:用.userInitiated队列优先级,保证预加载任务优先执行

内容的提问来源于stack exchange,提问作者whaleshark

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 06:35:23