Использовать Swift @propertyWrapper для динамического значения по умолчанию?

Мне нужно свойство Swift, которое, если значение еще не установлено, по умолчанию имеет другое значение.

Это можно реализовать с помощью частных свойств резервного хранилища. Например, для недвижимостиnum который по умолчанию должен быть глобальным defaultNum, это будет работать примерно так:

var defaultNum = 1

class MyClass {
  var num: Int {
    get { _num ?? defaultNum }
    set { _num = newValue }
   }

  private var _num: Int?
}

let c = MyClass()
print("initial \(c.num)") // == 1 ✅

// changing the default changes the value returned
defaultNum = 2
print("dynamic \(c.num)") // == 2 ✅

// once the property is set, returns the stored value
c.num = 5
print("base    \(c.num)") // == 5 ✅

Это работает, но для общего шаблона в нашем коде это много шаблонов для каждого такого свойства.

Можно ли сделать это более кратко, используя оболочки свойств Swift?


Что не сработает

Обратите внимание: поскольку мы ожидаем, что значение по умолчанию будет динамическим, статические инициализаторы не будут работать. Например:

var defaultNum = 1

class MyClass {
  var num = defaultNum
}

var c = MyClass()
defaultNum = 2
print(c.num) // this == 1, we want the current value of defaultNum, which == 2

1 ответ

Решение

Вы можете сделать это, создав такую ​​оболочку свойств:

@propertyWrapper
public struct Default<T> {
  var baseValue: T?
  var closure: () -> T

  // this allows a nicer syntax for single variables...
  public init(_ closure: @autoclosure @escaping () -> T) {
    self.closure = closure
  }

  // ... and if we want to explicitly use a closure, we can.
  public init(_ closure: @escaping () -> T) {
    self.closure = closure
  }

  public var wrappedValue: T {
    get { baseValue ?? closure() }
    set { baseValue = newValue }
  }
}

Затем вы используете @Default оболочка свойства для такого свойства:

var defaultNum = 1

class MyClass {
  @Default(defaultNum)
  var num: Int
}

Тогда вы увидите на практике следующее:

let c = MyClass()

// if we haven't set the property yet, it uses the closure to return a default value
print("initial \(c.num)") // == 1 ✅

// because we are using a closure, changing the default changes the value returned
defaultNum = 2
print("dynamic \(c.num)") // == 2 ✅

// once the property is set, uses the stored base value
c.num = 5
print("base    \(c.num)") // == 5 ✅
Другие вопросы по тегам