背景
開發(fā)過(guò)程正中可愛(ài)的產(chǎn)品總會(huì)給我們提一個(gè)這樣的需求

“這個(gè)頁(yè)面用戶第一次進(jìn)入的時(shí)候彈個(gè)框顯示一下我們的用戶協(xié)議,點(diǎn)完確認(rèn)之后不再顯示?!?/p>
收到這樣的需求腦子里第一個(gè)冒出的想法就是“簡(jiǎn)單,用在UserDefaults里放個(gè)key,每次進(jìn)來(lái)的時(shí)候取一下就好了,有就代表顯示過(guò)了,沒(méi)有就代表沒(méi)顯示過(guò)”
let value = UserDefaults.standard.object(forKey: "key1") as? Bool ?? false
if !value {
// do something
// ......
// ......
UserDefaults.standard.set(true, forKey: "key1")
UserDefaults.standard.synchronize()
}
然后過(guò)些日子產(chǎn)品又來(lái)這個(gè)地方再加個(gè)“使用規(guī)則”

于是上面的代碼換了個(gè)key復(fù)制了
然后再過(guò)些日子...,于是就出現(xiàn)了“key2”,“key3”,“key4”...上面的代碼寫得到處都是。等你回頭來(lái)review的時(shí)候發(fā)現(xiàn)...
@propertyWrapper
這個(gè)時(shí)候就可以用@propertyWrapper屬性包裝器來(lái)解決
@propertyWrapper // 標(biāo)記這個(gè)struct是個(gè)屬性包裝器
struct UserDefaultsWrapper<T> {
private let key: String
private let defaultValue: T
// 這個(gè)實(shí)例方法根據(jù)業(yè)務(wù)情況而定,也可以不實(shí)現(xiàn)
init(_ key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
// 必須實(shí)現(xiàn)的屬性
// 用這個(gè)代替被標(biāo)記屬性的set/get方法
var wrappedValue: T {
get {
return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
}
使用的時(shí)候
class ViewController1: UIViewController {
@UserDefaultsWrapper(key: "isCheckRule1", defaultValue: false)
var isCheckRule: Bool
// 因?yàn)槭欠盒停赃€可以是Array,Int,String等等
@UserDefaultsWrapper(key: "historySearchKeys", defaultValue: [])
var historySearchKeys: [String]
// ......
// ......
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !isDidCheckRule {
// do something
// ......
// ......
isDidCheckRule = true
}
// ......
// ......
for key in historySearchKeys {
// do something
}
}
}
// ......
// ......
class ViewController2: UIViewController {
@UserDefaultsWrapper(key: "isCheckRule2", defaultValue: false)
var isDidCheckRule: Bool
// ......
// ......
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !isDidCheckRule {
// do something
// ......
// ......
isDidCheckRule = true
}
}
}
上面被標(biāo)記的屬性
class ViewController1: UIViewController {
@UserDefaultsWrapper(key: "isCheckRule1", defaultValue: false)
var isCheckRule: Bool
}
等價(jià)于
class ViewController1: UIViewController {
var isDidCheckRule: Bool {
get {
return UserDefaults.standard.object(forKey: "isDidCheckRule1") as? Bool ?? false
}
set {
UserDefaults.standard.set(newValue, forKey: "isDidCheckRule1")
}
}
}
還可以這樣使用,比如需要去除前后空格
@propertyWrapper
struct FixString {
private var string: String?
// 只能作為計(jì)算屬性,不能作為存儲(chǔ)屬性
var wrappedValue: String? {
get {
return string
}
set {
// 去除前后空格
string = newValue?.trimmingCharacters(in: .whitespaces)
}
}
}
使用
class ViewController1: UIViewController {
@FixString
var text1: String
@FixString
var text2: String
@FixString
var text3: String
}
還可以限制一個(gè)值的范圍
@propertyWrapper
struct RangeWrapper<Number: Numeric> where Number: Comparable {
private var min: Number
private var max: Number
private var number: Number
var wrappedValue: Number {
get {
return number
}
set {
if newValue > max {
number = max
} else if newValue < min {
number = min
} else {
number = newValue
}
}
}
init(min: Number, max: Number, number: Number) {
self.min = min
self.max = max
self.number = number
}
}
使用
@RangeWrapper(min: 10, max: 100)
var number: Int
補(bǔ)充一點(diǎn)點(diǎn)
- 屬性包裝器可以用于類、結(jié)構(gòu)體、枚舉的屬性,不能用于局部和全局變量
- 可以不用實(shí)現(xiàn)構(gòu)造方法根據(jù)業(yè)務(wù)而定
- 擁有屬性包裝器的屬性可以包含 willSet 和 didSet 閉包,但是不能重寫編譯器合成的 get 和 set 閉包
- wrappedValue 只能作為計(jì)算屬性,不能作為存儲(chǔ)屬性
總結(jié)一下
可以看出@propertyWrapper其實(shí)就是用一個(gè)Struct幫我們?nèi)コ恍┲貜?fù)的set/get代碼,而且可以像普通的Struct一樣加入泛型,協(xié)議約束等,使用方法也非常簡(jiǎn)單,只要注意wrappedValue是必須實(shí)現(xiàn)的wrappedValue是必須實(shí)現(xiàn)的wrappedValue是必須實(shí)現(xiàn)的
最后
以上全是我個(gè)人不成熟的理解,有什么不對(duì)之處還希望有大佬能指正
完結(jié)撒花~
