Property Wrappers 是 Swift5.1语言的一个功能点,允许我们定义一个自定义的类型,实现get,set方法的一些包装,以便重用。那接下来你可能会提出几个问题?

  • 它是用来解决什么问题?
  • 怎样实现一个属性包裹?
  • 怎样去接收一个属性变量?
  • 属性包裹器被Swift编译器编译成什么了?
  • 属性包裹器有什么限制?

    理解属性包裹

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    struct Bar {
    private var _x = 0

    var x: Int {
    get { _x }
    set {
    _x = newValue
    print("New value is \(newValue)")
    }
    }
    }

    var bar = Bar()
    bar.x = 1 // Prints 'New value is 1'
    为了复用,我们将变量定义成泛型。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    struct ConsoleLogged<Value> {
    private var value: Value

    init(wrappedValue: Value) {
    self.value = wrappedValue
    }

    var wrappedValue: Value {
    get { value }
    set {
    value = newValue
    print("New value is \(newValue)")
    }
    }
    }
    Ok,我们用ConsoleLogged重写Bar
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct Bar {
    private var _x = ConsoleLogged<Int>(wrappedValue: 0)

    var x: Int {
    get { _x.wrappedValue }
    set { _x.wrappedValue = newValue }
    }
    }

    var bar = Bar()
    bar.x = 1 // Prints 'New value is 1'
    Swift提供@propertyWrapper关键字对以上封装。
    1
    2
    3
    4
    @propertyWrapper
    struct ConsoleLogged<Value> {
    // The rest of the code is unchanged
    }
    然后我们可以将bar的变量修改一下。
    1
    2
    3
    4
    5
    6
    struct Bar {
    @ConsoleLogged var x = 0
    }

    var bar = Bar()
    bar.x = 1 // Prints 'New value is 1'

    定义属性包裹器

  1. 必须有@propertyWrapper关键字。
  2. 必须有wrappedValue属性。
    1
    2
    3
    4
    @propertyWrapper
    struct Wrapper<T> {
    var wrappedValue: T
    }
    那么使用的时候就简单了
    1
    2
    3
    4
    5
    struct HasWrapper {
    @Wrapper var x: Int
    }

    let a = HasWrapper(x: 0)
    定义属性时候,我们有两种方式将属性传递给属性包裹器。
    1
    2
    3
    4
    struct HasWrapperWithInitialValue {
    @Wrapper var x = 0 // 方式一
    @Wrapper(wrappedValue: 0) var y // 方式二
    }

    接收属性

    我们可以在包裹器中扩展新增方法。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @propertyWrapper
    struct Wrapper<T> {
    var wrappedValue: T

    func foo() { print("Foo") }
    }

    struct HasWrapper {
    @Wrapper var x = 0

    func foo() { _x.foo() } //_x 是 Wrapper<T>的一个实例,我们可以在内部访问,但是不能再HasWrapper外部访问_x变量的。
    }
    属性包裹器可以通过projectedValue提供更多的API
    1
    2
    3
    4
    5
    6
    7
    8
    @propertyWrapper
    struct Wrapper<T> {
    var wrappedValue: T

    var projectedValue: Wrapper<T> { return self }

    func foo() { print("Foo") }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct HasWrapper {
    @Wrapper var x = 0

    func foo() {
    print(x) // `wrappedValue`
    print(_x) // wrapper type itself
    print($x) // `projectedValue`
    }
    }

    用例限制

  • 属性包裹器从Swift 5.1开始支持。
  • 属性包裹器修饰的属性不可被子类重写。
  • 属性包裹器修饰的属性不能与这些关键字一起使用 lazy, @NSCopying, @NSManaged, weak, or unowned.
  • 属性包裹器不能再有自己的get,set方法了。
  • 属性包裹器不能用于枚举,协议。

用例

UserDefaults
ValidatedPropertyKit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct LoginView: View {

@Validated(!.isEmpty && .isEmail)
var mailAddress = String()

@Validated(.range(8...))
var password = String()

var body: some View {
List {
TextField("E-Mail", text: self.$mailAddress)
TextField("Password", text: self.$password)
Button(
action: {
print("Login", self.mailAddress, self.password)
},
label: {
Text("Submit")
}
)
.validated(
self._mailAddress,
self._password
)
}
}
}

评论