Property Wrappers 是 Swift5.1语言的一个功能点,允许我们定义一个自定义的类型,实现get,set方法的一些包装,以便重用。那接下来你可能会提出几个问题?
- 它是用来解决什么问题?
- 怎样实现一个属性包裹?
- 怎样去接收一个属性变量?
- 属性包裹器被Swift编译器编译成什么了?
- 属性包裹器有什么限制?
理解属性包裹
为了复用,我们将变量定义成泛型。1
2
3
4
5
6
7
8
9
10
11
12
13
14struct 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'Ok,我们用ConsoleLogged重写Bar1
2
3
4
5
6
7
8
9
10
11
12
13
14
15struct 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)")
}
}
}Swift提供@propertyWrapper关键字对以上封装。1
2
3
4
5
6
7
8
9
10
11struct 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'然后我们可以将bar的变量修改一下。1
2
3
4
struct ConsoleLogged<Value> {
// The rest of the code is unchanged
}1
2
3
4
5
6struct Bar {
@ConsoleLogged var x = 0
}
var bar = Bar()
bar.x = 1 // Prints 'New value is 1'定义属性包裹器
- 必须有@propertyWrapper关键字。
- 必须有wrappedValue属性。那么使用的时候就简单了
1
2
3
4
struct Wrapper<T> {
var wrappedValue: T
}定义属性时候,我们有两种方式将属性传递给属性包裹器。1
2
3
4
5struct HasWrapper {
@Wrapper var x: Int
}
let a = HasWrapper(x: 0)1
2
3
4struct HasWrapperWithInitialValue {
@Wrapper var x = 0 // 方式一
@Wrapper(wrappedValue: 0) var y // 方式二
}接收属性
我们可以在包裹器中扩展新增方法。属性包裹器可以通过projectedValue提供更多的API1
2
3
4
5
6
7
8
9
10
11
12
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变量的。
}1
2
3
4
5
6
7
8
struct Wrapper<T> {
var wrappedValue: T
var projectedValue: Wrapper<T> { return self }
func foo() { print("Foo") }
}1
2
3
4
5
6
7
8
9struct 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
ValidatedPropertyKit1
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
27struct 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
)
}
}
}