Использовать 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 ✅