propertyWrapper - ShenYj/ShenYj.github.io GitHub Wiki
属性包装器最初是在 Swift 5.1 中引入的,作为一种以简单、可重用的方式将额外功能附加到属性的方式
例如,我们我们需要确保某个字段不能为负值
-
传统的实现方式:
class Member { var memberAge: Int { get { return self.age } set { if newValue < 0 { return 0 } else { self.age = newValue } } } private var age: Int init(){ self.age = 0 } }
-
利用属性包装器:
-
定义一个属性包装器,保存一个
Int
类型的值,并且不能低于0@propertyWrapper struct NonNegative { private var number: Int var wrappedValue: Int { get { return self.number } set { if newValue < 0 { self.number = 0 } else { self.number = newValue } } } init() { self.number = 0 } }
-
使用属性包装器修饰
属性
class Member { @NonNegative var age: Int init() { self.age = 0 } }
若这时我设置一个不满足条件的值时,为将其修正为 0
let m = Member() m.age = -1
-
在 Swift 5.4 中,它们的行为得到了扩展,以支持将它们用作函数中的局部变量。
@propertyWrapper
struct NonNegative<T: Numeric & Comparable> {
var value: T
var wrappedValue: T {
get { value }
set {
if newValue < 0 {
value = 0
} else {
value = newValue
}
}
}
init(wrappedValue: T) {
if wrappedValue < 0 {
self.value = 0
} else {
self.value = wrappedValue
}
}
}
从 Swift 5.4 开始,我们可以在常规函数中使用该属性包装器,而不仅仅是附加到一个属性上。例如,我们可能会编写一个游戏,其中我们的玩家可以得到或失去分数,但他们的分数不应低于 0:
func playGame() {
@NonNegative var score = 0
// player was correct
score += 4
// player was correct again
score += 8
// player got one wrong
score -= 15
// player got another one wrong
score -= 16
print(score)
}
playGame()
Property Wrapper 最早并不叫这个名字,而是叫做 Property Behavior,它来自于 Swift 最早期提案 SE-0030。如果看过 Apple Swift 源码的早期版本的话,可以发现里面有较多的 NSCopying 以及 lazy 等重复性的代码。核心团队为了解决这类重复代码问题就提出了 Property Behavior。可以看到这个提案的 State 是 deferred,可能当时核心团队还没有想好如何把这样一个能力做成一个Public 的 API 来对开发者开放。后来社区中有许多人发现这个能力还是很有必要的,可以解决项目中重复代码的问题,提升简洁度和可读性。于是有了 SE-0258 提案,并把提案中 Property Behavior 改名为 Property Wrapper。[摘自 Advanced Property Wrapper in Swift]