Swift協(xié)議:RawRepresentable

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ù)據(jù)時,需要指定key。
先定義一個協(xié)議, 該協(xié)議userDefaultsKey表示UserDefaults的key

public protocol UserDefaultsAccessable {
    var userDefaultsKey: String { get }
}

然后為UserDefaultsAccessable提供默認實現(xiàn),并增加幾個訪問UserDefaults的方法。包括存數(shù)據(jù)和取數(shù)據(jù)

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ù)據(jù)的存取方法
}

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ù)據(jù)時再將其還原。

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類型結(jié)構(gòu)體。

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)的方法,內(nèi)部通過位運算判斷是否包含某個值

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ù)組,猜測是編譯器在進行自動類型轉(zhuǎn)換,如果指定了Direction類型,自動轉(zhuǎn)換成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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容