微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

原子属性包装器仅在声明为类时才有效,而不是 struct

如何解决原子属性包装器仅在声明为类时才有效,而不是 struct

我在 Swift 中创建了一个“锁”,并为我的 Swift 类创建了一个使用该锁的 Atomic 属性包装器,因为 Swift 缺少 ObjC 的 atomic 属性属性

当我在启用线程清理器的情况下运行我的测试时,它总是捕获使用我的 Atomic 属性包装器的属性上的数据竞争。

唯一有效的是将属性包装器的声明更改为类而不是结构体,这里的主要问题是:为什么有效!

我在属性包装器中添加print 并锁定 init 以跟踪创建的对象数量,结构/类也是如此,尝试在另一个项目中重现该问题,但没有也不行。但是我会添加与问题类似的文件,并让我知道关于为什么会起作用的任何猜测。

锁定

public class SwiftLock {

    init() { }

   public func sync<R>(execute: () throws -> R) rethrows -> R {
    objc_sync_enter(self)
    defer { objc_sync_exit(self) }
    return try execute()
    }
}

原子属性包装器

@propertyWrapper struct Atomic<Value> {
    let lock: SwiftLock
    var value: Value

    init(wrappedValue: Value,lock: SwiftLock=SwiftLock()) {
        self.value = wrappedValue
        self.lock = lock
    }

    var wrappedValue: Value {
        get {
            lock.sync { value }
        }
        set {
            lock.sync { value = newValue }
        }
    }
}

模型(数据竞争应该发生在 publicVariable2 属性上)

class Model {
    @Atomic var publicVariable: TimeInterval = 0
    @Atomic var publicVariable2: TimeInterval = 0
    var sessionDuration: TimeInterval {
        min(0,publicVariable - publicVariable2)
    }
}

更新 1: 完整的 Xcode 项目:https://drive.google.com/file/d/1IfAsOdHKOqfuOp-pSlP75FLF32iVraru/view?usp=sharing

解决方法

这个问题在这个 PR 中得到了回答:https://github.com/apple/swift-evolution/pull/1387

我认为这些台词真正解释了它?

在Swift的正式内存访问模型中,值类型上的方法被认为是访问整个值,因此调用wrappedValue getter正式读取整个存储的包装器,而调用wrappedValue的setter正式修改整个存储的包装器。

包装器的值将在调用之前加载 wrappedValue.getter 并在调用后写回 wrappedValue.setter。因此,包装器内的同步 无法提供对其自身值的原子访问。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。