Может ли Swift Property Wrapper указывать на владельца объекта, который его упаковывает? - PullRequest
3 голосов
/ 20 июня 2019

В оболочке свойств в Swift, можете ли вы обратиться к экземпляру класса или к удару, которому принадлежит обернутое свойство? Использование self явно не работает, как и super.

Я пытался передать self в оболочку свойства init(), но это тоже не работает, потому что self в Configuration еще не определено при оценке @propertywrapper.

Мой вариант использования относится к классу для управления большим количеством настроек или конфигураций. Если какое-либо свойство изменяется, я просто хочу уведомить заинтересованные стороны, что что-то изменилось. Им действительно не нужно знать, какое значение просто, поэтому использовать что-то вроде KVO или Publisher для каждого свойства на самом деле не нужно.

Оболочка свойства выглядит идеально, но я не могу понять, как передать какую-то ссылку на экземпляр-владелец, которому обертка может вызывать обратно.

Рекомендации:

SE-0258

enum PropertyIdentifier {
  case backgroundColor
  case textColor
}

@propertyWrapper
struct Recorded<T> {
  let identifier:PropertyIdentifier
  var _value: T

  init(_ identifier:PropertyIdentifier, defaultValue: T) {
    self.identifier = identifier
    self._value = defaultValue
  }

  var value: T {
    get {  _value }
    set {
      _value = newValue

      // How to callback to Configuration.propertyWasSet()?
      //
      // [self/super/...].propertyWasSet(identifier)
    }
  }
}

struct Configuration {

  @Recorded(.backgroundColor, defaultValue:NSColor.white)
  var backgroundColor:NSColor

  @Recorded(.textColor, defaultValue:NSColor.black)
  var textColor:NSColor

  func propertyWasSet(_ identifier:PropertyIdentifier) {
    // Do something...
  }
}

1 Ответ

0 голосов
/ 25 июня 2019

Ответ - нет, это невозможно с текущей спецификацией.

Я хотел сделать что-то подобное.Лучшее, что я мог придумать, это использовать отражение в функции в конце init(...).По крайней мере, таким образом вы можете аннотировать ваши типы и добавлять только один вызов функции в init().


fileprivate protocol BindableObjectPropertySettable {
    var didSet: () -> Void { get set }
}

@propertyDelegate
class BindableObjectProperty<T>: BindableObjectPropertySettable {
    var value: T {
        didSet {
            self.didSet()
        }
    }
    var didSet: () -> Void = { }
    init(initialValue: T) {
        self.value = initialValue
    }
}

extension BindableObject {
    // Call this at the end of init() after calling super
    func bindProperties(_ didSet: @escaping () -> Void) {
        let mirror = Mirror(reflecting: self)
        for child in mirror.children {
            if var child = child.value as? BindableObjectPropertySettable {
                child.didSet = didSet
            }
        }
    }
}
...