Swift進階雜談6:列舉

語言: CN / TW / HK

C語言列舉的寫法回顧

在瞭解Swift的列舉之前,我們回顧下C語言的列舉寫法:

enum 列舉名 {
    列舉值1,
    列舉值2,
    ....
};

<!--舉例:表示一週7天-->
enum Weak{
    MON, TUE, WED, THU, FRI, SAT, SUN
};

<!--更改C中列舉預設值-->
//如果沒有設定列舉預設值,一般第一個列舉成員的預設值為整型0,後面依次遞推,如果我們想更改,只需要這樣操作
enum Weak{
    MON = 1, TUE, WED, THU, FRI, SAT, SUN
};

<!--C中定義一個列舉變數-->
//表明建立了一個列舉,並聲明瞭一個列舉變數weak
enum Weak{
    MON = 1, TUE, WED, THU, FRI, SAT, SUN
}weak;
//或者下面這種寫法,省略列舉名稱
enum{
    MON = 1, TUE, WED, THU, FRI, SAT, SUN
}weak;
複製程式碼

Swift中的列舉寫法類比

在swift中,列舉的建立方式如下,如果沒有指定列舉值的型別,那麼enum預設列舉值是整型

<!--1、寫法一-->
enum Weak{
    case MON
    case TUE
    case WED
    case THU
    case FRI
    case SAT
    case SUN
}

<!--2、寫法二-->
//也可以直接一個case,然後使用逗號隔開
enum Weak{
    case MON, TUE, WED, THU, FRI, SAT, SUN
}

<!--定義一個列舉變數-->
var w: Weak = .MON
複製程式碼

上訴程式碼中我們的列舉型別預設是整型,這個和C是一致的,如果我們想要表達的String怎麼辦?可以通過指定enum的列舉值的型別來建立,其中列舉值和原始值rawValue的關係為case 列舉值 = rawValue原始值

/*
 =左邊的值是列舉值,例如 MON
 =右邊的值在swift中稱為 RawValue(原始值),例如 "MON"
 兩者的關係為:case 列舉值 = rawValue原始值
*/
enum Weak: String{
    case MON = "MON"
    case TUE = "TUE"
    case WED = "WED"
    case THU = "THU"
    case FRI = "FRI"
    case SAT = "SAT"
    case SUN = "SUN"
}
複製程式碼
  • 如果不想寫列舉值後的字串,也可以使用隱式RawValue分配,隱式RawValue分配是建立在Swift的型別推斷機制上的,如下我們先用Int來舉例說明:
<!--Int型別-->
//MON是從0開始一次遞推,而WED往後是從10開始一次遞推
enum ShortDayWeak: Int{
    case mon, tue, wed, thu, fri = 10, sat, sun
}
複製程式碼

系統預設是從0開始,在這個過程中你可以指定fri = 10,那麼對於sat,系統會從根據前一個case的值來做累加的操作,也就是11。

如果只是對於Int型別適用,對於String型別也是一樣適用的。 接下來我們要區分一個東西,我們看以下程式碼的列印結果

enum DayOfWeek:String {
    case monday, tuesday, wednesday, thursday, friday, saturday, sunday
}
print(DayOfWeek.monday) //這裡輸出的是什麼
複製程式碼

通過執行得知,這裡輸出為monday。這裡需要注意的一點就是列舉值RawValue是兩個不同的東西,比如我們沒有辦法把一個列舉值分配一個String的變數,即使這個enumString型別。 與此同時,我們也沒辦法把一個具體型別的資料分配給具體的列舉型別,比如如下例子報錯 所以上面print出來的就是具體的列舉值,而通過rawValue訪問的就是rawValueget方法

  • 如果enum沒有宣告型別,是沒有rawValue屬性的

列舉的遍歷

那我們的列舉是不是可以像集合那樣遍歷?答案是可以的

enum week:String {
    case mon, tue, wed, thu, fri = "Hello", sat, sun
}

extension week:CaseIterable{}
var allCase = week.allCases
for c in allCase {
    print(c)
}
複製程式碼

列印如下

關聯值 Associated Value Enumerations

上面可以看到隱式分配會給每一個Case分配一個隱式值,但是有時候我們並不想這麼做,或者有時候我們想通過enum來表達更復雜的例子,關聯更多的資訊。這個時候我們需要使用到關聯值了。 比如我們通過列舉值來表達一個形狀,有圓形,長方形,正方形。圓形有半徑,長方形有寬,高,這個時候關聯值就顯得非常有用了

enum Shape {
    case circle(radius: Double)
    case rectangle(width: Int, height: Int)
}
複製程式碼

注意⚠️這裡我們使用了關聯值之後,就沒有RawValue。這裡也非常好理解,因為當前一個case都可以有一組值來表示。 當然這裡的radius、height、width是我們取的標籤,如果你不想寫,那就變成下面這樣:

enum Shape {
    case circle(Double)
    case rectangle(Int, Int)
}

複製程式碼

可以通過以下方法建立一個有關聯值的列舉值

enum Shape {
    case circle(radius: Double)
    case rectangle(width: Int, height: Int)
}
//******使用的時候也非常簡單,這裡就可以直接給定值來建立一個關聯的列舉值
var circle = Shape.circle(radius: 10.0)
var rectangle = Shape.rectangle(width: 10, height: 10)
//******也可以重新分配值
circle = Shape.rectangle(width: 10, height: 10)
複製程式碼

列舉的其他用法

模式匹配

enum Week:String {
    case MONDAY
    case TUEDAY
    case WEDDAY
    case THUDAY
    case FRIDAY
    case SATDAY
    case SUNDAY
}

let currentWeek:Week = .FRIDAY

switch currentWeek{
    case .MONDAY: print(Week.MONDAY.rawValue)
    case .TUEDAY: print(Week.TUEDAY.rawValue)
    case .WEDDAY: print(Week.WEDDAY.rawValue)
    case .THUDAY: print(Week.THUDAY.rawValue)
    case .FRIDAY: print(Week.FRIDAY.rawValue)
    case .SATDAY: print(Week.SATDAY.rawValue)
    case .SUNDAY: print(Week.SUNDAY.rawValue)
}
複製程式碼

使用Switch匹配enum的時候我們必須列舉當前所有的情況,不然編譯器就會報錯: 如果不想匹配這麼多的case,那麼我們就可以使用default關鍵字來代表預設的情況

enum Week:String {
    case MONDAY
    case TUEDAY
    case WEDDAY
    case THUDAY
    case FRIDAY
    case SATDAY
    case SUNDAY
}

let currentWeek:Week = .FRIDAY

switch currentWeek{
    case .SATDAY, .SUNDAY: print("Happy Day")
    default : print("Sad Day")

}
複製程式碼

如果我們要匹配關聯值的話

enum Shape {
    case circle(radius: Double)
    case rectangle(width: Int, height: Int)
}

let shape = Shape.circle(radius: 10.0)

switch shape{
    /*這裡我們做了Value-binding,也就意味著如果case匹配上,我們相當於把10賦值給常量radious*/
    case let .circle(radius):
        print("Circle radius:\(radius)")
        break
    case let .rectangle(width, height):
        print("rectangle width:\(width),height\(height)")
        break
    
}
複製程式碼

還可以這麼寫

enum Shape {
    case circle(radius: Double)
    case rectangle(width: Int, height: Int)
}

let shape = Shape.circle(radius: 10.0)

switch shape{
    case .circle(let radius):
        print("Circle radius:\(radius)")
        break
    case .rectangle(let width, var height):
        print("rectangle width:\(width),height\(height)")
        break
    
}
複製程式碼

有時候在業務邏輯處理中我們只是想匹配單個case,我們可以這樣寫

var circle = Shape.circle(radius: 10.0)
if case let Shape.circle(radius) = circle {
    print("Circle radius:\(radius)")
}
複製程式碼

如果我們只關心不同case的相同關聯值,我們可以這麼寫

enum Shape {
    case circle(radius: Double, diameter: Double)
    case rectangle(width: Double, height: Double)
    case square(width: Double, width:Double)
}
let shape = Shape.circle(radius: 10.0, diameter: 20.0)
switch shape {
    case let .circle(x, 20.0), let .square(x, 20.0):
        print(x)
        break
    default:
        break
}
複製程式碼

或者使用萬用字元的方式:

let shape = Shape.circle(radius: 10.0, diameter: 20.0)
switch shape {
    case let .circle(_, x), let .square(x, _):
        print(x)
        break
    default:
        break
}
/**/
switch shape{
case let .circle(x, y), let .square(y, x):
    print("x = \(x),y = \(y)")
    break
default:
    break
}
複製程式碼

列舉巢狀

比如我們的遊戲案例,不同的案件組合會有不同的技能產生

enum CombineDirect{
    //基礎按鍵就4個
    enum BaseDirect{
        case up
        case down
        case left
        case right
    }
    //帶來的組合按鍵
    case leftUp(combineElement1: BaseDirect, combineElement2: BaseDirect)
    case rightUp(combineElement1: BaseDirect, combineElement2: BaseDirect)
    case leftDown(combineElement1: BaseDirect, combineElement2: BaseDirect)
    case rightDown(combineElement1: BaseDirect, combineElement2: BaseDirect)
}
//使用方式
let leftUp = CombineDirect.leftUp(combineElement1: CombineDirect.BaseDirect.left, combineElement2: CombineDirect.BaseDirect.up)
複製程式碼

結構體中的巢狀

struct Skill{

   enum KeyType{
          case up
          case down
          case left
          case right
   }


    let key: KeyType

    func launchSkill(){
        switch key {
        case .left,.right:
            print("left, right")
        case .down,.up:
            print("up, down")
        }
    }
}
複製程式碼

Enum中包含的屬性

enum中能夠包含計算屬性型別屬性,不能包含儲存屬性,這裡簡單瞭解一下:

Enum中包含方法

我們也可以在enum中定義例項方法static修飾的方法

enum Week:Int {
    case MONDAY
    case TUEDAY
    case WEDDAY
    case THUDAY
    case FRIDAY
    case SATDAY
    case SUNDAY
    
    mutating func nextDay(){
        if self == .SUNDAY {
            self = Week(rawValue: 1)!
        }else{
            self = Week(rawValue: self.rawValue + 1)!
        }
    }
    
}

var currentWeek:Week = .FRIDAY
print("old current  \(currentWeek),rawValue \(currentWeek.rawValue)")
currentWeek.nextDay()
print("new current  \(currentWeek),rawValue \(currentWeek.rawValue)")
print("end")

<------**輸出結果**---->
old current  FRIDAY,rawValue 4
new current  SATDAY,rawValue 5
end
複製程式碼

總結

  • 1、enum中使用rawValue的本質是呼叫get方法,即在get方法中從Mach-O對應地址中取出字串並返回的操作
  • 2、enum中init方法的呼叫是通過列舉.init(rawValue:)或者列舉(rawValue:)觸發的
  • 3、沒有關聯值的enum,如果希望獲取所有列舉值,需要遵循CaseIterable協議,然後通過列舉名.allCase的方式獲取
  • 4、case列舉值和rawValue原始值的關係:case 列舉值 = rawValue原始值
  • 5、具有關聯值的列舉,可以成為三無enum,因為沒有別名RawValue、init、計算屬性rawValue
  • 6、enum的模式匹配方式,主要有兩種:switch / if case
  • 7、enum可以巢狀enum,也可以在結構體中巢狀enum,表示該enum是struct私有的
  • 8、enum中還可以包含計算屬性、型別屬性,但是不能包含儲存屬性
  • 9、enum中可以定義例項 + static修飾的方法