propertyWrapper下标未调用。为什么?

发布于 2025-01-22 09:57:25 字数 1009 浏览 1 评论 0原文

我正在实现自己的atomicdictionary属性包装器如下:

@propertyWrapper
public class AtomicDictionary<Key: Hashable, Value>: CustomDebugStringConvertible {
  public var wrappedValue = [Key: Value]()

  private let queue = DispatchQueue(label: "atomicDictionary.\(UUID().uuidString)",
                                    attributes: .concurrent)

  public init() {}

  public subscript(key: Key) -> Value? {
    get {
      queue.sync {
        wrappedValue[key]
      }
    }

    set {
      queue.async(flags: .barrier) { [weak self] in
        self?.wrappedValue[key] = newValue
      }
    }
  }

  public var debugDescription: String {
    return wrappedValue.debugDescription
  }
}

现在,当我使用它的使用如下:

class ViewController: UIViewController {
  @AtomicDictionary var a: [String: Int]

  override func viewDidLoad() {
    super.viewDidLoad()
    self.a["key"] = 5
  }
}

atomicdicationary的订阅函数未调用!

有人对为什么这样做有任何解释吗?

I am implementing my own AtomicDictionary property wrapper as follows:

@propertyWrapper
public class AtomicDictionary<Key: Hashable, Value>: CustomDebugStringConvertible {
  public var wrappedValue = [Key: Value]()

  private let queue = DispatchQueue(label: "atomicDictionary.\(UUID().uuidString)",
                                    attributes: .concurrent)

  public init() {}

  public subscript(key: Key) -> Value? {
    get {
      queue.sync {
        wrappedValue[key]
      }
    }

    set {
      queue.async(flags: .barrier) { [weak self] in
        self?.wrappedValue[key] = newValue
      }
    }
  }

  public var debugDescription: String {
    return wrappedValue.debugDescription
  }
}

now, when I use it as follows:

class ViewController: UIViewController {
  @AtomicDictionary var a: [String: Int]

  override func viewDidLoad() {
    super.viewDidLoad()
    self.a["key"] = 5
  }
}

The subscript function of the AtomicDicationary is not called!!

Does anybody have any explanation as to why that is?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

作业与我同在 2025-01-29 09:57:25

属性包装器 仅提供基本访问者方法,基本访问者方法,但就是这样。它不会拦截下标或其他方法。

原始属性包装纸提案向我们展示了幕后发生的事情。它考虑了一个假设的属性包装器,懒惰,其中:

属性声明

  @lazy var foo = 1738
 

转换为:

  private var _foo:lazy&lt; int&gt; = Lazy&lt; int&gt;(包装值:1738)
var foo:int {
    获取{return _foo.wrappedvalue}
    设置{_foo.wrappedvalue = newValue}
}
 

请注意,foo只是int计算属性。 _fooLazy&lt; int&gt;

因此,在您的A [“ KEY”] = 5示例中,它不会使用您的属性包装器的下标操作员。它将获取a关联的值,使用字典自己的下标操作员更新该值(而不是属性包装器的下标运算符),然后将设置a关联的值。

这就是所有属性包装器所做的,提供getset配件。例如,声明:

@AtomicDictionary var a: [String: Int]

转换为:

private var _a: AtomicDictionary<String, Int> = AtomicDictionary<String, Int>(wrappedValue: [:])
var a: [String: Int] {
    get { return _a.wrappedValue }
    set { _a.wrappedValue = newValue }
}

您定义的任何其他方法仅通过_a在本示例中访问,而不是a(这只是一个计算属性BrappedValue _a)。


因此,最好只是为“原子词典”定义适当的类型:

public class AtomicDictionary<Key: Hashable, Value> {
    private var wrappedValue: [Key: Value]
    
    private let queue = DispatchQueue(label: "atomicDictionary.\(UUID().uuidString)", attributes: .concurrent)
    
    init(_ wrappedValue: [Key: Value] = [:]) {
        self.wrappedValue = wrappedValue
    }
    
    public subscript(key: Key) -> Value? {
        get {
            queue.sync {
                wrappedValue[key]
            }
        }
        
        set {
            queue.async(flags: .barrier) {
                self.wrappedValue[key] = newValue
            }
        }
    }
}

let a = AtomicDictionary<String, Int>()

为您提供了想要的行为。


而且,如果您要提供CustomDebugStringConvertible一致性,请确保在此处使用您的同步机制:

extension AtomicDictionary: CustomDebugStringConvertible {
    public var debugDescription: String {
        queue.sync { wrappedValue.debugDescription }
    }
}

所有与包装值的交互都必须同步。


显然,您可以将此通用模式与所需的任何同步机制一起使用,例如上述阅读器撰写的模式,GCD串行队列,锁,演员等(读取器撰写的模式具有自然吸引力,但实际上,在那里, 的机制。


通常是更好 人们应该始终对通用线程安全集合保持警惕,因为我们代码的正确性依赖于更高级别的同步。

Property wrappers merely provide an interface for the basic accessor methods, but that’s it. It’s not going to intercept subscripts or other methods.

The original property wrapper proposal SE-0258 shows us what is going on behind the scenes. It contemplates a hypothetical property wrapper, Lazy, in which:

The property declaration

@Lazy var foo = 1738

translates to:

private var _foo: Lazy<Int> = Lazy<Int>(wrappedValue: 1738)
var foo: Int {
    get { return _foo.wrappedValue }
    set { _foo.wrappedValue = newValue }
}

Note that foo is just an Int computed property. The _foo is the Lazy<Int>.

So, in your a["key"] = 5 example, it will not use your property wrapper’s subscript operator. It will get the value associated with a, use the dictionary’s own subscript operator to update that value (not the property wrapper’s subscript operator), and then it will set the value associated with a.

That’s all the property wrapper is doing, providing the get and set accessors. E.g., the declaration:

@AtomicDictionary var a: [String: Int]

translates to:

private var _a: AtomicDictionary<String, Int> = AtomicDictionary<String, Int>(wrappedValue: [:])
var a: [String: Int] {
    get { return _a.wrappedValue }
    set { _a.wrappedValue = newValue }
}

Any other methods you define are only accessible through _a in this example, not a (which is just a computed property that gets and sets the wrappedValue of _a).


So, you’re better off just defining a proper type for your “atomic dictionary”:

public class AtomicDictionary<Key: Hashable, Value> {
    private var wrappedValue: [Key: Value]
    
    private let queue = DispatchQueue(label: "atomicDictionary.\(UUID().uuidString)", attributes: .concurrent)
    
    init(_ wrappedValue: [Key: Value] = [:]) {
        self.wrappedValue = wrappedValue
    }
    
    public subscript(key: Key) -> Value? {
        get {
            queue.sync {
                wrappedValue[key]
            }
        }
        
        set {
            queue.async(flags: .barrier) {
                self.wrappedValue[key] = newValue
            }
        }
    }
}

And

let a = AtomicDictionary<String, Int>()

That gives you the behavior you want.


And if you are going to supply CustomDebugStringConvertible conformance, make sure to use your synchronization mechanism there, too:

extension AtomicDictionary: CustomDebugStringConvertible {
    public var debugDescription: String {
        queue.sync { wrappedValue.debugDescription }
    }
}

All interaction with the wrapped value must be synchronized.


Obviously you can use this general pattern with whatever synchronization mechanism you want, e.g., the above reader-writer pattern, GCD serial queue, locks, actors, etc. (The reader-writer pattern has a natural appeal, but, in practice, there are generally better mechanisms.)


Needless to say, the above presumes that subscript-level atomicity is sufficient. One should always be wary about general purpose thread-safe collections as often the correctness of our code relies on a higher-level of synchronization.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文