Shaula Blog

美丽的东西都是肤浅的

Swift 中的 @preconcurrency

随着 Swift 并发模型的成熟,开发者在集成旧代码或第三方库时面临着一个挑战:如何在严格的并发检查下使用非并发安全的 API?@preconcurrency 属性包装器正是为解决这一问题而生。

什么是 @preconcurrency

@preconcurrency 是从 Swift 5.6 开始引入的一个修饰符,它允许开发者以更宽松的方式导入尚未完全适配 Swift 严格并发检查的模块。它不是 SwiftUI 独有的,但在 SwiftUI 开发中特别有用,因为 SwiftUI 应用常常需要集成各种异步数据源和第三方库。

为什么需要 @preconcurrency

1. 严格的并发检查

Swift 6 引入了更严格的并发检查

swiftSettings: [
    .enableExperimentalFeature("StrictConcurrency")
]

默认情况下会:

  • 禁止在非主线程访问 UI 相关代码
  • 强制标记可能跨隔离域访问的数据
  • 检查 Sendable 一致性

2. 现实世界的代码迁移

许多现有库和框架尚未完全适配这些严格的规则,但开发者仍需要在 Swift 6 项目中使用它们。

使用场景

场景一:导入旧版库

@preconcurrency import LegacyNetworkLibrary

class DataViewModel: ObservableObject {
    @Published var data: [String] = []
    
    func fetchData() async {
        let result = await LegacyNetworkLibrary.fetchFromNetwork()
        await MainActor.run {
            self.data = result
        }
    }
}

场景二:针对单个类型

@preconcurrency 
@MainActor 
public final class SomeViewModel {
  public private(set) var items: [Item] = []

  public init() {}

  public func loadData() {
    //...
  }
}

注意事项

1. 不是长期解决方案

@preconcurrency 应该被视为迁移工具,而非永久解决方案:

// 应该做的事:
// 1. 使用 @preconcurrency 让代码通过编译
// 2. 向库维护者报告并发问题
// 3. 计划迁移到并发安全版本

// 不应该做的事:
// 依赖 @preconcurrency 作为永久解决方案

2. 性能考虑

使用 @preconcurrency 可能意味着错过编译器的并发优化:

// 没有 @preconcurrency - 编译器可以进行更多优化
func optimizedFetch() async -> Data {
    return await ModernSDK.fetch()
}

// 使用 @preconcurrency - 优化机会减少
@preconcurrency
func lessOptimizedFetch() async -> Data {
    return await LegacySDK.fetch()
}

3. 线程安全责任

当使用 @preconcurrency 时,你需要自己确保线程安全:

@preconcurrency import NonThreadSafeSDK

class DataManager {
    private let sdkInstance: NonThreadSafeSDK.Instance
    private let queue = DispatchQueue(label: "com.example.datamanager")
    
    // 需要手动处理线程安全
    func updateData() async {
        await withCheckedContinuation { continuation in
            queue.async {
                // 确保在正确的队列上访问
                self.sdkInstance.update()
                continuation.resume()
            }
        }
    }
}

总结

@preconcurrency 是 Swift 并发迁移过程中的重要工具,它提供了以下功能:

  1. 兼容性:允许在严格并发检查下使用旧代码
  2. 渐进迁移:让团队可以逐步更新代码库

但请记住, @preconcurrency 只是通往这个目标的桥梁,而不是目的地。