屬性包裝器
屬性包裝器在管理屬性如何存儲和定義屬性的代碼之間添加了一個分隔層。舉例來說,如果你的屬性需要線程安全性檢查或者需要在數(shù)據(jù)庫中存儲它們的基本數(shù)據(jù),那么必須給每個屬性添加同樣的邏輯代碼。當(dāng)使用屬性包裝器時,你只需在定義屬性包裝器時編寫一次管理代碼,然后應(yīng)用到多個屬性上來進(jìn)行復(fù)用。
比如定義一個包裝器TestA,TestA確保其值在大于10的時候返回10,小于10的時候就返回對應(yīng)的值。
在Test A這個例子中,我們還定義了一個構(gòu)造方法,給num賦初始值。在使用的時候,我們可以通過@TestA(numValue: 5) var width: Int 這種方式給對應(yīng)的裝飾器賦值,這句話相當(dāng)于width的初始值為5。
我們還可以通過$符號來獲取裝飾器的一個呈現(xiàn)值。在TestA中projectedValue就是呈現(xiàn)值,通過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
}
}
現(xiàn)在定義一個類SCRange,它有屬性width和height,但是我們在使用了TestA這個裝飾器去修飾這2個屬性的時候,就相當(dāng)于規(guī)定了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)
}
// 結(jié)果打?。?
// 結(jié)果打?。?0
// 結(jié)果打印:true
綜合例子:
在下面這個例子,定義了一個線程安全的Int類型,在多線程的訪問中可以保證數(shù)據(jù)被安全的操作。我們定義了一個賣票的類Ticket和subTicket方法,總票數(shù)ticketNum是100張,同時有多個線程在賣票,這個時候我們就要保重多個線程對總票數(shù)的安全操作,不會多賣或少賣。
在ThreadSafeNum這個裝飾器中,我們定義了NSLock的鎖,對num的讀寫我們都加了鎖,保證數(shù)據(jù)讀寫多線程的安全。
@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()
}
}
}