从Swift5.0开始,Swift引入了属性包装器(propertyWrapper),这应该算是苹果原生应用开发上的一次技术思想的突破吧。即便功能限制性很多,但也为我们解决一些问题带来了方便。比如,我们现在要限制一些数值的取值范围的话,在Objc时代,甚至是Swift早些时候。我们要想要实现这种方法,那么久的在赋值或者取值的过程中进行判断。这从代码上来讲还是侵入性比较强的。但是在Swift5.0引入属性包装器之后,这类问题就得到了解决。下面我们就来一步步了解属性包装器的使用吧。
为了更好的来讨论,我们先设定一个场景:在一个用户信息系统中,我们要存储用户的基本信息,但是要求用户信息中的年龄字段取值只能在0~120之间,如果年龄大于120则取120,小于0则取0,要求使用的时候要保证数据取值的安全性。这个问题我们应该怎么解决呢?
在属性包装器概念引入之前,为了数据的安全性我们基本上在每个使用到年龄大地方,都要写一大堆保护性代码,或者调用一个专门的封装函数来进行处理,如果涉及到某些地方用年龄来过滤或做表,可能还存在一些其他更复杂的问题。当然,为了统一,我们可能会写一个如下的方法:
func fixAge(age: Int) -> Int {
if age > 120 {
return 120
}
if age < 0 {
return 0
}
return age
}
在使用的时候通过fixAge(age: )调用来达到数据修正的目的。但是,这是一个好办法么?如果我们从代码侵入性角度来看肯定是不好的。下面,我们用属性包装器来实现:
@propertyWrapper
struct AgeWrapper {
var wrappedValue: Int {
get {
return age
}
set {
if newValue > max {
age = max
} else if newValue < min {
age = min
} else {
age = newValue
}
}
}
var age: Int = 0
var min: Int
var max: Int
init(min: Int, max: Int) {
self.min = min
self.max = max
}
}
接下来,我们再来定义变量age
@AgeWrapper(min: 0, max: 120) var age: Int
并给一个调用的实例如下:
print("old value -> \(age)")
age = 139
print("new value -> \(age)")
运行代码会得到如下运行结果
old value -> 0
new value -> 120
好想,我们在使用的时候,完全没有做任何修正,但是我们前面的目的已经达到了。不仅如此,为了实现对年龄限制的的通用性,我们对属性包装器做如下修改:
@propertyWrapper
struct AgeWrapper {
var wrappedValue: Int {
get {
return age
}
set {
age = limitHandle(newValue)
}
}
var age: Int = 0
var limitHandle: (Int) -> Int
init(limit handle: @escaping (Int) -> Int) {
self.limitHandle = handle
}
}
并将初始化修改如下:
@AgeWrapper(limit: { max(0, min($0, 120)) }) var age: Int
继续运行执行结果,我们会发现,执行的结果与之前是一致的。而且这个包函数也变的更灵活了。
有了一个真实的案例,回过来在看看属性包装器的实现逻辑:
- 属性包装器的定义基于一个被@propertyWrapper修饰的struct或class的;
- 属性包装起有一个必须实现一个名为wrappedValue的属性;该属性实际负责对真实数据进行修正处理
- 在使用定义的属性包装器时,将@属性包装器 放在需要修饰的属性前即可;
- 最后就是使用被修饰的变量,只需按照我们正常的使用变量的方式使用即可。
回过头来想想,属性包装器,对于我们已有的未做数据安全保护的代码,是不是现在要进行数据安全性保护的话,将变得更容易呢?
网友评论