属性包装器
属性包装器在管理属性如何存储和定义属性的代码之间添加了一个分隔层。举例来说,如果你的属性需要线程安全性检查或者需要在数据库中存储它们的基本数据,那么必须给每个属性添加同样的逻辑代码。当使用属性包装器时,你只需在定义属性包装器时编写一次管理代码,然后应用到多个属性上来进行复用。
比如定义一个包装器TestA,TestA确保其值在大于10的时候返回10,小于10的时候就返回对应的值。
在Test A这个例子中,我们还定义了一个构造方法,给num赋初始值。在使用的时候,我们可以通过@TestA(numValue: 5) var width: Int
这种方式给对应的装饰器赋值,这句话相当于width的初始值为5。
我们还可以通过$
符号来获取装饰器的一个呈现值。在TestA中projectedValue
就是呈现值,通过range1.$width
来访问。
@propertyWrapper
struct TestA {
var num: Int
var projectedValue: Bool
var wrappedValue: Int {
get {
return num
}
set {
if newValue > 10 {
num = 10
projectedValue = true
}else {
num = newValue
}
}
}
init(numValue: Int) {
num = numValue
projectedValue = false
}
}
现在定义一个类SCRange,它有属性width和height,但是我们在使用了TestA这个装饰器去修饰这2个属性的时候,就相当于规定了width和height的最大值就是10。
class SCRange {
@TestA(numValue: 5) var width: Int
@TestA(numValue: 5) var height: Int
}
func test1() {
let range1: SCRange = SCRange()
print(range1.width)
range1.width = 50
print(range1.width)
print(range1.$width)
}
// 结果打印:5
// 结果打印:10
// 结果打印:true
综合例子:
在下面这个例子,定义了一个线程安全的Int类型,在多线程的访问中可以保证数据被安全的操作。我们定义了一个卖票的类Ticket和subTicket方法,总票数ticketNum是100张,同时有多个线程在卖票,这个时候我们就要保重多个线程对总票数的安全操作,不会多卖或少卖。
在ThreadSafeNum这个装饰器中,我们定义了NSLock的锁,对num的读写我们都加了锁,保证数据读写多线程的安全。
@propertyWrapper
struct ThreadSafeNum {
private var num: Int
let myLock: NSLock = NSLock()
var wrappedValue: Int {
get {
myLock.lock()
defer {myLock.unlock()}
return num
}
set {
myLock.lock()
defer {myLock.unlock()}
num = newValue
}
}
init(saftNum: Int) {
num = saftNum
}
}
class Ticket {
@ThreadSafeNum(saftNum: 100) var ticketNum: Int
func subTicket() {
ticketNum = ticketNum - 1
if ticketNum <= 0 {
print("没票了")
}else {
print("还剩\(ticketNum)")
}
}
}
func test2() {
let ticket: Ticket = Ticket()
for _ in 0...199 {
DispatchQueue.global().async {
ticket.subTicket()
}
}
}
网友评论