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

Swift 6中如何实现并发安全的静态属性操作?

问题描述

我有一个基类A及其子类SubA_x,基类A包含静态属性serialNumber,该属性仅在子类初始化时由A修改,用于为每个子类实例设置唯一的name属性。相关代码如下:

class A {
    static var serialNumber = 0
    let name: String

    init( /* some parameters */ ) {
        A.serialNumber += 1
        self.name = "\(A.serialNumber)"
    }
}

final class SubA_1: A {
    init( /* some parameters */ ) {
        super.init( /* some parameters */ )
    }
}

在Swift 6严格并发检查下,修改serialNumber的代码行出现错误提示:

Static property 'serialNumber' is not concurrency-safe because it is
non-isolated global shared mutable state

我清楚子类可能在任意线程修改serialNumber,存在数据竞争风险。但如何以Swift并发框架原生方式实现该功能的并发安全?

我尝试用Actor管理serialNumber:

actor SerialNumberManager {
    private var serialNumber = 0
    
    func getNextSerialNumber() -> Int {
        serialNumber += 1
        return serialNumber
    }
}

但无法在非异步的init中调用getNextSerialNumber();若将init改为异步,就只能在异步上下文初始化子类。我不想依赖GCD,希望用Swift原生并发框架解决问题。

解决方案

方案1:使用Swift标准库Lock实现互斥访问

Swift 5.7+的Foundation框架提供了Lock类型,它是并发安全的互斥锁,可直接保护共享可变状态,完全符合Swift并发安全要求,且无需修改初始化方法的同步特性。修改基类A的代码如下:

import Foundation

class A {
    private static let lock = Lock()
    private static var serialNumber = 0
    let name: String

    init(/* some parameters */) {
        let nextNumber: Int
        Self.lock.withLock {
            Self.serialNumber += 1
            nextNumber = Self.serialNumber
        }
        self.name = "\(nextNumber)"
    }
}

final class SubA_1: A {
    init(/* some parameters */) {
        super.init(/* some parameters */)
    }
}

Lock.withLock会自动处理锁的加锁与解锁逻辑,确保同一时间只有一个线程能修改serialNumber,子类可正常在任意上下文初始化。

方案2:使用Actor结合同步等待(不推荐,可能阻塞线程)

如果一定要基于Actor实现,可以通过Task的同步等待方式在非异步init中获取序列号,但这种方式会阻塞当前线程,可能影响并发性能,仅适合对性能要求较低的场景:

actor SerialNumberManager {
    static let shared = SerialNumberManager()
    private var serialNumber = 0
    
    func getNextSerialNumber() -> Int {
        serialNumber += 1
        return serialNumber
    }
}

class A {
    let name: String

    init(/* some parameters */) {
        // 同步等待Actor返回序列号,会阻塞当前线程
        let nextNumber = Task.detached(priority: .userInitiated) {
            await SerialNumberManager.shared.getNextSerialNumber()
        }.value
        self.name = "\(nextNumber)"
    }
}

final class SubA_1: A {
    init(/* some parameters */) {
        super.init(/* some parameters */)
    }
}

注意:该方式会阻塞调用init的线程,若在主线程调用可能导致UI卡顿,因此更推荐方案1。

内容的提问来源于stack exchange,提问作者Reinhard Männer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 15:53:20