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

如何确保Swift Manager类在API数据拉取完成后同步初始化

解决Swift DataManager初始化时等待异步数据加载的问题

问题核心

你的代码在init中启动了Task执行异步数据拉取,但Swift的初始化是同步完成的,shared实例会立刻创建完成,此时data仍为空数组,后续提前访问fetchedData就会拿到空值,这是问题的根本原因。

可行解决方案

方案1:异步静态方法创建单例(推荐)

将单例的创建改为异步方法,确保数据加载完成后才返回可用实例,从根源避免空数据问题:

import Foundation

class DataManager {
    static private var _shared: DataManager?
    private let apiService: APIService
    private let data: [String]

    // 私有初始化,仅允许通过异步方法创建实例
    private init(apiService: APIService, initialData: [String]) {
        self.apiService = apiService
        self.data = initialData
    }

    // 异步单例获取方法
    static func shared() async throws -> DataManager {
        if let shared = _shared {
            return shared
        }
        let apiService = APIService()
        let initialData = try await apiService.fetchData()
        let manager = DataManager(apiService: apiService, initialData: initialData)
        _shared = manager
        return manager
    }

    var fetchedData: [String] {
        return data
    }
}

使用示例:
在异步上下文内调用,确保拿到的实例已加载完成数据:

// 在ViewModel或其他异步代码块中
let manager = try await DataManager.shared()
let loadedData = manager.fetchedData // 此时数据一定已准备好

方案2:异步数据访问方法(兼容现有单例结构)

保持现有初始化逻辑,但将数据访问改为异步方法,确保访问时等待数据加载完成:

class DataManager {
    static let shared = DataManager()
    private let apiService: APIService
    private var data: [String]?
    private let loadLock = NSLock()

    private init(apiService: APIService = APIService()) {
        self.apiService = apiService
        // 后台启动数据加载
        Task {
            try await loadInitialData()
        }
    }

    private func loadInitialData() async throws {
        loadLock.lock()
        defer { loadLock.unlock() }
        if data != nil { return } // 避免重复加载
        data = try await apiService.fetchData()
    }

    // 异步获取数据,保证返回时已加载完成
    func getFetchedData() async throws -> [String] {
        if let existingData = data {
            return existingData
        }
        try await loadInitialData()
        return data!
    }
}

使用示例:

let loadedData = try await DataManager.shared.getFetchedData()

方案3:同步等待异步任务(不推荐,易死锁)

如果必须在init内完成数据加载,可以用信号量强制等待,但主线程调用会导致死锁,仅适合非主线程初始化场景:

private init(apiService: APIService = APIService()) {
    self.apiService = apiService
    let semaphore = DispatchSemaphore(value: 0)
    
    Task {
        do {
            self.data = try await self.fetchInitialData()
            print("Initial data fetched: \(self.data)")
        } catch {
            print("Error fetching initial data: \(error)")
        }
        semaphore.signal()
    }
    
    // 等待异步任务完成,注意:主线程调用会死锁!
    semaphore.wait()
}

选择建议

  • 优先采用方案1,符合Swift异步编程设计理念,确保实例完全可用后才对外暴露,彻底避免空值隐患。
  • 方案2适合无法修改单例创建逻辑的场景,通过异步访问方法保证数据可用性。
  • 方案3尽量避免,容易引发死锁和主线程阻塞,严重影响App性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 09:40:12