enum 中的RawRepresentable
RawRepresentable是一個協(xié)議雪情,包含關聯(lián)類型RawValue RawValue相當于一個范型簇秒,遵循這個協(xié)議相當于該對象擁有了一個“原始值”揩页。我們知道在定義enum時奋单,如果在冒號后面指定一個類型如 Int,則enum擁有了rawValue(原始值),因為它遵循了RawRepresentable協(xié)議然爆。
下面定義一個枚舉。
enum User: String {
case isLogin
case loginTime
}
User.isLogin.rawValue 值為字符串”isLogin“黍图,而rawValue能保證在此枚舉中是唯一的曾雕。
我們可以利用這個特性設計一些需要保證唯一性的功能,而且這能做字符串編譯器檢查助被,否則手寫字符串會導致拼寫錯誤剖张,代碼更加簡潔方便。
我們可以將此特性引用于UserDefault中揩环,當需要往UserDefaults存數(shù)據時搔弄,需要指定key。
先定義一個協(xié)議, 該協(xié)議userDefaultsKey表示UserDefaults的key
public protocol UserDefaultsAccessable {
var userDefaultsKey: String { get }
}
然后為UserDefaultsAccessable提供默認實現(xiàn)丰滑,并增加幾個訪問UserDefaults的方法顾犹。包括存數(shù)據和取數(shù)據
public extension UserDefaultsAccessable where Self: RawRepresentable, Self.RawValue == String {
// 以此為UserDefaults的key
var userDefaultsKey: String {
"\(Self.self).\(rawValue)"
}
/// 取bool 值
func boolVal() -> Bool {
return UserDefaults.standard.bool(forKey: userDefaultsKey)
}
/// 存儲
func storeBool(_ value: Bool) {
return UserDefaults.standard.setValue(value, forKey: userDefaultsKey)
}
///... 下面還可以增加其他類型數(shù)據的存取方法
}
where Self: RawRepresentable, Self.RawValue == String 這句話對協(xié)議進行限定表示遵循這個協(xié)議的對象必須遵循RawRepresentable協(xié)議,并且RawValue類型為String。這樣就能在協(xié)議的默認實現(xiàn)里使用RawRepresentable的rawValue屬性炫刷,很強大擎宝。
這里的是可以利用范型的,如存方法可以改成
/// 存儲
func store<T: Codable>(_ value: T) {
....
}
遵循了Codable 就可以將對象encode to字符串浑玛,然后保存到UserDefaults绍申,取數(shù)據時再將其還原。
func _encode<T: Codable>(_ value: T) -> String? {
do {
let data = try JSONEncoder().encode([value])
return String(String(data: data, encoding: .utf8)!.dropFirst().dropLast())
} catch {
print(error)
return nil
}
}
使用時定義一個枚舉顾彰,并遵循UserDefaultsAccessable協(xié)議
extension UserDefaults {
enum User: String, UserDefaultsAccessable {
case isLogin
case loginWay
}
}
外面包一個UserDefaults是一個命名空間极阅,防止與其他類型同名,這樣User就擁有存取特性了拘央;可以這樣使用:
//存
UserDefaults.User.isLogin.storeBool(true)
//取
UserDefaults.User.isLogin.boolVal()
這樣用起來是不是更加簡單方便呢涂屁,還能保證key的唯一性书在。
OptionSet 中的RawRepresentable
OptionSet也遵循了RawRepresentable協(xié)議灰伟,OptionSet都知道類似于c語言的可選枚舉,它通過或運算組合多個值儒旬;下面定義一個OptionSet類型結構體栏账。
struct Direction: OptionSet {
let rawValue: Int
static let left = Direction(rawValue: 1 << 0)
static let right = Direction(rawValue: 1 << 1)
static let bottom = Direction(rawValue: 1 << 2)
static let tp = Direction(rawValue: 1 << 3)
static let all: Direction = [.left, .right, .bottom, .top]
}
let rawValue: Int是RawRepresentable的協(xié)議,必須遵循栈源,如何使用呢挡爵?通過contains方法,他是OptionSet的一個默認實現(xiàn)的方法甚垦,內部通過位運算判斷是否包含某個值
var left: Direction = .left
let right = Direction.right
let left_right = [Direction.left, Direction.right]
let left_right_top: Direction = [.left, [.top, .right]]
let allDir: Direction = .all
left.insert(.bottom)
print(left.contains(.bottom))
print(right.contains(.left))
print(left_right.contains(.left))
print(left_right_top.contains(.top))
print(allDir.contains(.bottom))
這里有一個很奇怪的事情let left_right: Direction = [.left, .right]這句如果改成let left_right = [Direction.left, Direction.right]茶鹃,則left_right的類型變成了數(shù)組,猜測是編譯器在進行自動類型轉換艰亮,如果指定了Direction類型闭翩,自動轉換成Direction。
還有一種用法迄埃,我們可以通過init(rawValue:)進行初始化, 因為它遵循了RawRepresentable的協(xié)議疗韵。
let dir = Direction(rawValue: 22)
這種初始化方式不會失敗,我們知道enum(有原始值)也可以通過這種方式初始化侄非,如上面的enum
let other = UserDefaults.User(rawValue: "other")
print(other)
這種初始化是可能失敗的蕉汪,因為OptionSet沒有所有可能情況的枚舉列表,而枚舉有逞怨。選項集值與其關聯(lián)的原始值有一一對應關系者疤。
print(left_right_top.rawValue)