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

Posted

技术标签:

【中文标题】原子属性包装器仅在声明为类而不是结构时才有效【英文标题】:Atomic property wrapper only works when declared as class, not struct 【发布时间】:2021-04-14 20:03:04 【问题描述】:

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

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

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

我在属性包装器中添加了prints 并锁定inits 以跟踪创建的对象数量,与结构/类相同,尝试在另一个项目中重现问题,也没有工作.但我会添加与问题相似的文件,并让我知道为什么会起作用的任何猜测。

锁定

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

【问题讨论】:

感谢您提供所有代码,但您能提供一个minimal reproducible example 吗?为了测试,我们需要做什么? 我在问题中添加了项目的链接。它包括问题中提到的代码示例 + 带有模拟场景的测试的测试目标。再次值得一提的是,我没有设法重现这个项目的问题,不幸的是我无法分享原始代码。 我想这是因为 objc_sync_* 方法要求它们的参数具有标识。结构没有那个,只有类。 @***soft 但锁中的self 指的是SwiftLock 而不是Atomic 我相信this 的帖子——标题为“objc_sync_enter / objc_sync_exit not working with DISPATCH_QUEUE_PRIORITY_LOW”——足以回答这个问题。看Sir Wellington在讨论中的回答。基本上,问题的出现是因为 Swift 结构是值类型。在同一个讨论中还建议使用 GCD 而不是 objc_sync_enter,因为后者是旧的并且非常低级 【参考方案1】:

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

我认为这是真正解释它的那些台词?

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

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

【讨论】:

以上是关于原子属性包装器仅在声明为类而不是结构时才有效的主要内容,如果未能解决你的问题,请参考以下文章

css margin-top 属性仅在声明边框时才有效[重复]

为啥java的一些概念需要使用包装类而不是原始数据类型[重复]

日期选择器仅在聚焦时显示错误

Swift | 属性包装器

为啥 Thread.Abort 仅在 isBackground 属性设置为 true 时才有效?

我可以为类而不是实例定义 __repr__ 吗? [复制]