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
)
}
}
}

评论