使用 Swift @propertyWrapper 作为动态默认值?
Use Swift @propertyWrapper for dynamic default value?
我需要一个 Swift 属性 -- 如果尚未设置该值 -- 默认为另一个值。
这可以使用后备存储私有属性来实现。例如,对于默认为全局 defaultNum
的 属性 num
,它会像这样工作:
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
您可以像这样创建一个 属性 包装器来做到这一点:
@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 ✅
我需要一个 Swift 属性 -- 如果尚未设置该值 -- 默认为另一个值。
这可以使用后备存储私有属性来实现。例如,对于默认为全局 defaultNum
的 属性 num
,它会像这样工作:
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
您可以像这样创建一个 属性 包装器来做到这一点:
@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 ✅