對Swift協議RawRepresentable的理解

前言

對於枚舉原始值,系統默認只能是字符串、整型、浮點型字面量,那麼咱們能不能增長其它類型呢?swift

答案是能夠的,經過協議RawRepresentable就能夠實現xcode

正文

首先定義一個枚舉函數

enum Terrain: String {
    case forest = "F"
    case mountain = "M"
    case water = "W"
}
複製代碼

一、RawRepresentable協議

該協議定義了一個初始化構造器,當傳入無效的原始值時構造器會返回nil;還定義了一個rawValue計算屬性。ui

當定義上述枚舉時,編譯器將生成以下等價的代碼:spa

enum Terrain {
    case forest
    case mountain
    case water
}

extension Terrain: RawRepresentable {
    typealias RawValue = String

    init?(rawValue: RawValue) {
        switch rawValue {
        case "F": self = .forest
        case "M": self = .mountain
        case "W": self = .water
        default: return nil
        }
    }

    var rawValue: RawValue {
        switch self {
        case .forest: return "F"
        case .mountain: return "M"
        case .water: return "W"
        }
    }
}
複製代碼

二、rawValue本質

rawValue本質是一個計算屬性.net

enum Terrain: String {
    case forest = "F"
    case mountain = "M"
    case water = "W"
}
let tt = Terrain.forest
print(tt.rawValue)
複製代碼
0x104e32fe2 <+130>: movq   %rdx, -0x50(%rbp)
0x104e32fe6 <+134>: callq  0x104e330b0               ; Test.Terrain.rawValue.getter : Swift.String at <compiler-generated>
0x104e32feb <+139>: movq   0x6056(%rip), %rsi        ; (void *)0x00007fff879c41c8: type metadata for Swift.String
複製代碼

經過斷點調試彙編能夠看到,底層call調用了rawValue的getter方法調試

而對rawValue進行賦值則會報錯rest

let tt = Terrain.forest
tt.rawValue = "M" //Cannot assign to property: 'rawValue' is immutable
複製代碼

三、原始值不會改變枚舉的內存

一、原始值是不會存儲到枚舉變量中去的,而且只佔用一個字節(嚴格的說當枚舉成員個數小於256)code

方法(1)經過函數MemoryLayout能夠驗證blog

MemoryLayout.size(ofValue: Terrain.forest) // → 1個字節
MemoryLayout.size(ofValue: "F") // → 16個字節
複製代碼

方法(2)經過xcode調試View Memory能夠查看枚舉變量在內存中具體存放的內容(已有很多文章介紹,就不細說了)

四、增長原始值類型

RawRepresentable的RawValue關聯類型能夠是任何類型,前提是字面量,即它們在編譯時必須是靜態已知的。

而且實際原始值能夠在運行時動態建立。舉一個用於顏色的枚舉示例:

import UIKit

// 默認這裏不能直接用UIColor做爲原始值
enum Color {
    case red
    case green
    case blue
}

// 經過遵照協議RawRepresentable能夠把UIColor做爲原始值
extension Color: RawRepresentable {
    typealias RawValue = UIColor

    init?(rawValue: RawValue) {
        switch rawValue {
        case UIColor.red: self = .red
        case UIColor.green: self = .green
        case UIColor.blue: self = .blue
        default: return nil
        }
    }

    var rawValue: RawValue {
        switch self {
        case .red: return UIColor.red
        case .green: return UIColor.green
        case .blue: return UIColor.blue
        }
    }
}

// 可使用UIColor
Color.blue.rawValue is UIColor
複製代碼

最後

一、RawRepresentable協議不單單用在枚舉上,還有Swift中的選項集合OptionSet(該協議繼承自RawRepresentable),應用於位掩碼操做

二、原始值和關聯值是不一樣的;原始值是在定義枚舉時被預先填充的值,像上述三個字符串。對於一個特定的枚舉成員,它的原始值始終不變。關聯值是建立一個基於枚舉成員的常量或變量時才設置的值,枚舉成員的關聯值能夠變化。底層以及應用它們的區別還有不少,這裏就不細說了。

參考:oleb.net/blog/2016/1…

相關文章
相關標籤/搜索