使用Swift @propertyWrapper作为动态默认值?

时间:2019-11-18 20:22:27

标签: swift swift5.1 property-wrapper

我需要一个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 个答案:

答案 0 :(得分:1)

您可以通过以下方式创建属性包装器来实现此目的:

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

  init(_ closure: @escaping () -> T) {
    self.closure = closure
  }

  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 ✅