Swift 枚舉

前言

本篇文章將講述Swift中很常用的也很重要的一個知識點 ?? Enum枚舉。首先會介紹與OC中枚舉的差別,接著會從底層分析Enum的使用場景,包含枚舉的嵌套遞歸與OC的混編的場景,最后分析枚舉的內存大小的計算方式,希望大家能夠掌握。

一、OC&Swift枚舉的區別

1.1 OC中的NS_ENUM

OC中的枚舉和C/C++中的枚舉基本一樣,具有以下特點??

  • 僅支持Int類型,默認首元素值為0,后續元素值依次+1
  • 中間的元素有賦值,那么以此賦值為準,后續沒賦值的元素值依舊依次+1
    枚舉使用示例代碼??
typedef NS_ENUM(NSInteger, WEEK) {
    Mon,
    Tue = 10,
    Wed,
    Thu,
    Fri,
    Sat,
    Sun
};
// 調用代碼??
WEEK a = Mon; 
WEEK b = Tue;
NSLog(@"a = %d, b = %d", (int)a, (int)b);

1.2 Swift中的Enum

Swift中的枚舉比OC的強大很多!其特點如下??

  • 格式: 不用逗號分隔,類型需使用case聲明
  • 內容:
    1. 支持Int、Double、String基礎類型,也有默認枚舉值String類型默認枚舉值為key的名稱,Int、Double數值型默認枚舉值為0開始+1遞增
    2. 支持自定義選項 ?? 不指定支持類型,就沒有rawValue,但同樣支持case枚舉,可自定義關聯內容

注意:rawValue在后面枚舉的訪問中會詳細的講解。

示例代碼??

// 寫法一
// 不需要逗號隔開
enum Weak1 {
    case MON
    case TUE
    case WED
    case THU
    case FRI
    case SAT
    case SUN
}

// 寫法二
// 也可以直接一個case,然后使用逗號隔開
enum Weak2 {
    case MON, TUE, WED, THU, FRI, SAT, SUN
}

// 定義一個枚舉變量
var w: Weak1 = .MON

1.2.2 自定義選項類型

如果在聲明枚舉時不指定類型,那么可給枚舉項添加拓展內容(即自定義類型)。switch-case訪問時,可取出拓展類型進行相應的操作。例如??

// 自定義類型的使用
enum Shape {
    case square(width: Double)
    case circle(radius: Double, borderWidth:Double)
}

func printValue(_ v: Shape) {
    // switch區分case(不想每個case處理,可使用default)
    switch v {
    case .square(let width):
        print(width)
    case .circle(let radius, _):
        print(radius)
    }
}

let s = Shape.square(width: 10)
let c = Shape.circle(radius: 20, borderWidth: 1)
printValue(s)
printValue(c)

二、Swift枚舉的使用

接下來我們看看Swift枚舉的使用,包含一些特殊的場景的情況。

2.1 枚舉的訪問

說到枚舉的訪問,就必須得提一個關鍵字rawValue,使用案例??

enum Weak: String{
    case MON, TUE, WED, THU, FRI, SAT, SUN
}
var w = Weak.MON.rawValue
print(w)

運行結果??


注意:如果enum沒有聲明類型,是沒有rawValue屬性的??

現在問題來了 ?? rawValue對應在底層是如何做到讀取到MON值的?

rawValue的取值流程

老規矩,找入口,之前我們都是查看SIL,當然這里也不例外??

swiftc -emit-sil xx.swift | xcrun swift-demangle >> ./xx.sil && vscode xx.sil

先看看枚舉Week??

接著看看main函數的流程??

最后看看rawValue的getter方法??

然后看bb8代碼段??

至此,我們現在知道了,rawValue的底層就是調用的getter方法,getter方法中構造了字符串,但是這個字符串的值(例如“MON”)從哪里取出的呢?其實我們能猜出來,應該是在編譯期確定了的,所以,我們打開工程的exec可執行文件,查看Mach-O??

可見,在__TEXT, __cstring的section段,這些字符串在編譯期已經存儲好了,而且內存地址是連續的。所以,rawValue的getter方法 ?? case分支中構建的字符串,主要是在Mach-O文件中從對應地址取出的字符串,然后再返回給變量w。

case值 & rawValue值

現在我們弄清楚了rawValue值的來源,那么又有一個問題:枚舉case值和 rawValue值如何區分呢?下面的代碼輸出打印結果是什么?

//輸出 case值
print(Weak.MON)
//輸出 rawValue值
print(Weak.MON.rawValue)

雖然輸出的都是MON,但其實并不是相同的,why?看下圖??

上圖可知,并不能將枚舉的case值賦給字符串類型常量w,同時,也不能將字符串"MON"賦給枚舉值t。

2.2 枚舉初始化init

在OC中,枚舉沒有初始化一說,而在Swift中,枚舉是有init初始化方法??

Weak.init(rawValue:)

接下來我們來看看這個初始化方法在底層的流程,首先添加代碼??,打上斷點

print(Weak.init(rawValue: "MON")!)

打開匯編,運行??

接著我們還是看SIL代碼,關于Weak.init(rawValue:)部分??

其中,上圖中涉及的SIL的指令釋義??

指令名稱 指令釋義
index_addr 獲取當前數組中的第n個元素值的地址(即指針),存儲到當前地址中
struct_extract 表示在結構體中取出當前的Int值,Int類型在系統中也是結構體
cond_br 表示比較的表達式,即分支條件跳轉(類似于三元表達式)

接著來看看這個關鍵的函數_findStringSwitchCase的源碼??

我們繼續看Weak.init的最終處理代碼 ?? bb29代碼段??

至此,我們分析完了Weak.init的底層流程,于是修改之前的調用代碼(去掉了之前的感嘆號!)??

print(Weak.init(rawValue: "MON"))
print(Weak.init(rawValue: "Hello"))

編譯器會爆出警告(返回的結果是可選型),運行結果??

所以,現在我們就能明白,為什么一個打印的是可選值,一個打印的是nil。

2.3 枚舉遍歷:CaseIterable協議

CaseIterable協議,有allCases屬性,支持遍歷所有case,例如??

// Double類型
enum Week1: Double, CaseIterable {
    case Mon,Tue, Wed, Thu, Fri, Sat, Sun
}
Week1.allCases.forEach { print($0.rawValue)}

// String類型
enum Week2: String, CaseIterable {
    case Mon,Tue, Wed, Thu, Fri, Sat, Sun
}
Week2.allCases.forEach { print($0.rawValue)}

2.4 枚舉關聯值

關聯值就是上面講過的自定義類型的枚舉,它能表示更復雜的信息,與普通類型的枚舉不同點在于??

  1. 沒有rawValue
  2. 沒有rawValue的getter方法
  3. 沒有初始化init方法

例如

// 自定義類型的使用
enum Shape {
    case square(width: Double)
    case circle(radius: Double, borderWidth:Double)
}

查看其SIL代碼??

中間層代碼真的什么都沒有!??

2.5 模式匹配

模式匹配就是針對case的匹配,根據枚舉類型,分為2種:

  1. 簡單類型的枚舉的模式匹配
  2. 自定義類型的枚舉(關聯值)的模式匹配

2.5.1 簡單類型

swift中的簡單類型enum匹配需要將所有情況都列舉,或者使用default表示默認情況,否則會報錯!

enum Weak: String{
    case MON
    case TUE
    case WED
    case THU
    case FRI
    case SAT
    case SUN
}

var current: Weak?
switch current {
    case .MON:print(Weak.MON.rawValue)
    case .TUE:print(Weak.MON.rawValue)
    case .WED:print(Weak.MON.rawValue)
    default:print("unknow day")
}

如果去掉default,會報錯??

我們看看SIL代碼??

所以運行上面代碼,應該匹配的是default分支??

2.5.2 關聯值類型

關聯值類型的模式匹配有兩種方式??

  • switch - case ?? 匹配所有case
  • if - case ?? 匹配單個case
switch - case
enum Shape{
    case circle(radius: Double)
    case rectangle(width: Int, height: Int)
}

let修飾case值??

let shape = Shape.circle(radius: 10.0)
switch shape{
    //相當于將10.0賦值給了聲明的radius常量
    case let .circle(radius):
        print("circle radius: \(radius)")
    case let .rectangle(width, height):
        print("rectangle width: \(width) height: \(height)")
}

也可以let var修飾關聯值的入參??

let shape = Shape.circle(radius: 10.0)
switch shape{
    case .circle(let radius):
        print("circle radius: \(radius)")
    case .rectangle(let width, var height):
        height += 1
        print("rectangle width: \(width) height: \(height)")
}

查看SIL層的代碼,看看是怎么匹配的??

if - case
let circle = Shape.circle(radius: 10.0)
if case let Shape.circle(radius) = circle {
    print("circle radius: \(radius)")
}
通用關聯值

如果只關心不同case下的某一個關聯值,可以將該關聯值用同一個入參替換,例如下面例子中的x??

enum Shape{
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
    case square(width: Double, height: Double)
}
let shape = Shape.circle(radius: 10)
switch shape{
case let .circle(x), let .square(20, x):
    print(x)
default:
    print("未匹配")
    break
}

注意:不能使用多于1個的通用入參,例如下面的y??

也可以使用通配符 _??

let shape = Shape.rectangle(width: 10, height:20)
switch shape{
case let .rectangle(_, x), let .square(_, x):
    print("x = \(x)")
default:
    break
}

還可以這么寫??

let shape = Shape.rectangle(width: 10, height:20)
switch shape{
case let .rectangle(x, _), let .square(_, x):
    print("x = \(x)")
default:
    break
}

大家平時在使用枚舉時,還是要注意下面2點??

  1. 枚舉使用過程中不關心某一個關聯值,可以使用通配符_標識
  2. OC只能調用Swift中Int類型的枚舉

2.6 支持計算型屬性 & 函數

Swift枚舉中還支持計算屬性函數,例如??

enum Direct: Int {
    case up
    case down
    case left
    case right
    
    // 計算型屬性
    var description: String{
        switch self {
        case .up:
            return "這是上面"
        default:
            return "這是\(self)"
        }
    }
    
    // 函數
    func printSelf() {
        print(description)
    }
}

Direct.down.printSelf() 

三、枚舉嵌套

枚舉的嵌套主要有2種場景??

  1. 枚舉嵌套枚舉
  2. 結構體嵌套枚舉

3.1 enum嵌套enum

我們先來看看枚舉嵌套枚舉,我們繼續改下上面的例子??

enum CombineDirect{
    //枚舉中嵌套的枚舉
    enum BaseDirect{
        case up
        case down
        case left
        case right
    }
    //通過內部枚舉組合的枚舉值
    case leftUp(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case leftDown(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case rightUp(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case rightDown(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
}

如果初始化一個左上方向,代碼??

let leftUp = CombineDirect.leftUp(baseDIrect1: CombineDirect.BaseDirect.left, baseDirect2: CombineDirect.BaseDirect.up)

3.2 struct嵌套enum

接下來就是結構體嵌套枚舉了,例如??

//結構體嵌套枚舉
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 .up, .down:
            print("up, down")
        }
    }
}

3.3 枚舉的遞歸(indirect)

還有一種特殊的場景 遞歸 ?? 枚舉中case關聯內容使用自己的枚舉類型。例如??

enum Binary<T> {
    case empty
    case node(left: Binary, value:T, right:Binary)
}

一個結構,其左右節點的類型也是自己本身,這時編譯器會報錯??

報錯原因 ?? 使用該枚舉時,enum的大小需要case來確定,而case的大小又需要使用到enum大小。所以無法計算enmu的大小,于是報錯!
安排 ?? 根據編譯器提示,需要使用關鍵字indirect,意思就是將該枚舉標記位遞歸,同時也支持標記單個case,所以可以??

那么問題來了,indirect在底層干了什么呢?

indirect底層原理

我們先來看這個例子??

enum List<T>{
    case end
    indirect case node(T, next: List<T>)
}

var node = List<Int>.node(10, next: List<Int>.end)

print(MemoryLayout.size(ofValue: node))
print(MemoryLayout.stride(ofValue: node))

size和stride都是8,換成String類型??

仍然也是8,看來枚舉的大小不受其模板類型大小的影響。

lldb分析

我們先lldb看看其內存的分布??

上圖中我們發現,node的metadata對應的地址0x0000000100562660是分配在上的,所以,indirect關鍵字其實就是通知編譯器,需要分配一塊堆區的內存空間,用來存放enumcase。此時case為node時,存儲的是引用地址0x0000000100562660,而case為end時,則??

那為何說地址是堆區呢?我們接著看看SIL代碼??

SIL代碼中,是通過alloc_box申請的內存,alloc_box底層調用的是swift_allocObject,所以是堆區,我們可以再node打上斷點,查看匯編??

四、##swift和OC混編枚舉

接下來我們看看swift和OC的枚舉的混編場景。

4.1 OC使用Swift枚舉

首先看看OC調用Swift枚舉,那么此時枚舉必須具備以下2個條件??

  1. @objc關鍵字標記enum
  2. 當前enum必須是Int類型
// Swift中定義枚舉
@objc enum Weak: Int{
    case MON, TUE, WED, THU, FRI, SAT, SUN
}

// OC使用
- (void)test{
    Weak mon = WeakMON;
}

4.2 Swift使用OC枚舉

反過來,就沒限制了,OC中的枚舉會自動轉換成swift中的enum。

// OC定義
NS_ENUM(NSInteger, OCENUM){
    Value1,
    Value2
};

// swift使用
//1、將OC頭文件導入橋接文件
#import "OCFile.h"
//2、使用
let ocEnum = OCENUM.Value1
typedef enum
// OC定義
typedef enum {
    Num1,
    Num2
}OCNum;

// swift使用
let ocEnum = OCNum.init(0)
print(ocEnum)

上圖可知,通過typedef enum定義的enum,在swift中變成了一個結構體,并遵循了兩個協議:EquatableRawRepresentable

typedef NS_ENUM
// OC定義
typedef NS_ENUM(NSInteger, OCNum) {
    Num1,
    Num2
};
// swift使用
let ocEnum = OCNum.init(rawValue: 0)
print(ocEnum!)

那么自動生成的swift中是這樣??

并沒有遵循任何協議!

4.3 OC使用Swift中String類型的枚舉

這也是一種常見的場景,解決方案??

  1. swift中的enum盡量聲明成Int整型
  2. 然后OC調用時,使用的是Int整型的
  3. enum再聲明一個變量/方法,用于返回固定的字符串,給swift中使用
    示例??
@objc enum Weak: Int{
    case MON, TUE, WED
    
    var val: String?{
        switch self {
        case .MON:
            return "MON"
        case .TUE:
            return "TUE"
        case .WED:
            return "WED"
        default:
            return nil
        }
    }
}

// OC中使用
Weak mon = WeakMON;

// swift中使用
let weak = Weak.MON.val

五、枚舉的大小

主要分析以下幾種情況??

  1. 普通enum
  2. 具有關聯值的enum
  3. enum嵌套enum
  4. struct嵌套enum

枚舉的大小也是面試中經常問到的問題,重點在于兩個函數的區別??

size: 實際占用內存大小
stride:系統分配的內存大小

5.1 普通enum

最普通的情況,即非嵌套,非自定義類型的枚舉,例如??

enum Weak {
    case MON
}

print(MemoryLayout<Weak>.size)
print(MemoryLayout<Weak>.stride)

再添加一個case,運行??

繼續增加多個case,運行??

以上可以看出,當case個數為1時,枚舉size為0,個數>=2時,size的大小始終是1,why?下面我們來分析分析??

上圖打斷點,讀取內存可以看出,case都是1字節大小,1個字節是8個byte,按照二進制轉換成十進制,那么有255種排列組合(0x00000000 - 0x11111111),所以當case為1個的時候,size的大小是0(二進制是0x0),case數<=255時,size都是1。而超過255個時,會自動擴容sizestride都會增加

5.2 具有關聯值的enum

如果是自定義類型的枚舉,即關聯值類型,size和stride的值會發生什么變化呢?看下面的例子??

enum Shape{
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
}
print(MemoryLayout<Shape>.size)
print(MemoryLayout<Shape>.stride)

看來關聯值的枚舉大小和關聯值入參有關系,??

5.3 enum嵌套enum

枚舉嵌套枚舉,是一種特殊的情況,下面示例大小是多少???

enum CombineDirect{
    enum BaseDirect{
        case up, down, left, right
    }
    
    case leftUp(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case rightUp(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case leftDown(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case rightDown(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
}

print(MemoryLayout<CombineDirect>.size)
print(MemoryLayout<CombineDirect>.stride)

從結果中可以看出,enum嵌套enum,和具有關聯值的enum的情況是一樣的,同樣取決于關聯值的大小,其內存大小是最大關聯值的大小。
接著我們看看具體的分布,可以先定義一個變量??

var combine = CombineDirect.leftDown(baseDirect1: .left, baseDirect2: .down)

lldb查看其內存分布??

將第一個入參left 改為 up??

所以,02表示是caseleftDown的關聯值的第一個入參的枚舉值,那第2個入參是.down,按照規律來算應該是01,但卻是81,why?接下來我們看看81代表的是什么值?

  • 在enum CombineDirect中多加4個case

結果是c1

  • 減少一個case

減少一個case項后,是a1

  • 再減少一個case

再減少一個是81,說明81中的8

  • 繼續減少,保證case只有2個

結果頁是81

  • 添加保證case>10個

如果leftDown 的case索引值大于8,例如上圖,leftDown是第1個case,結果e1中的e就是15(十進制),即case選項的索引值。

  • 再減少,保證case leftDown在第9個

果然,上圖中leftDown的case索引值是9,打印出來的91中的第一位也是9。

綜上, 81中的第一位8這個值,有以下幾種情況區分??

  1. 當嵌套enum的case只有2個時,case在內存中的存儲是0、8
  2. 當嵌套enum的case大于2,小于等于4時,case在內存中的存儲是0、4、8、12
  3. 當嵌套enum的case大于4時,case在內存中的存儲是從0、1、2...類推

81中的1,代表什么意思呢?我們改變下關聯值入參??

所以,leftDown減少一個入參,結果是80,加一個入參,結果是01 80,繼續再加一個入參??

關聯值的入參是up,down,right,right,對應的枚舉值是0,1,3,3,所以可以得出結論??

  1. enum嵌套enum同樣取決于最大case的關聯值大小
  2. case中關聯值的內存分布,又是根據入參的個數大小來分布的
    2.1 每個入參占一個字節大小的空間(即2個byte位),第2位byte里面存儲的是內層枚舉的case值,第1位的byte值通常是0
    2.2 最后一個入參的byte空間分布 ?? 第2位是內層枚舉的case值,第1位是外層枚舉的case值,其規律又如下??
  • 當外層enum的case只有2個時,第1位byte值按照0、8依次分布
  • 當外層enum的case個數>2,<=4時,第1位byte值按照0、4、8、12依次分布
  • 當外層enum的case個數>4時,第1位byte值按照0、1、2、3、...依次分布

5.4 struct嵌套enum

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 .up, .down:
            print("up, down")
        }
    }
}
print(MemoryLayout<Skill>.size)
print(MemoryLayout<Skill>.stride)

size和stride都是1。結構體的大小計算,跟函數無關,所以只看成員變量key的大小,key是枚舉Skill類型,大小為1,所以結構體大小為1。繼續,去掉成員key??

沒有任何成員變量時,size為0,stride為1(系統默認分配的)。如果加一個成員??

因為添加的是UInt8,占1個字節,所以size和stride都+1,均為2。再添加一個成員??

添加的成員是Int類型,占8字節,8+1+1=10,而stride是系統分配的,8的倍數來分配,所以是16。你以為就這么簡單的相加嗎?我們換一下width的位置??

將width成員放到最后面,size變為16,why?因為size的大小,是按照結構體內存對齊原則來計算的,可參考我之前的文章內存對齊分析。

總結

本篇文章主要講解了Swift中的枚舉,開始與OC的枚舉作比較,引出Swift枚舉的不同點,進而分析了rawValue和初始化init的底層實現流程,然后講解了幾個重要的場景 ?? OC和Swift的橋接場景,枚舉嵌套的場景,最后重點分析了枚舉的大小,即內存分布的情況,這也是面試中經常出的題目,希望大家掌握,謝謝!

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內容

  • C語言枚舉 一周七天可以寫成 第?個枚舉成員的默認值為整型的 0,后?的枚舉值依次類推,如果我們想更改,只需要這樣...
    Mjs閱讀 241評論 0 1
  • C語言的枚舉 C語言的枚舉寫法 我們通過枚舉表示一周的七天 c語言中,枚舉的第一個成員默認是為0,后面的枚舉值一次...
    浪的出名閱讀 404評論 0 1
  • 首先感覺Swift的枚舉確實相對OC來說功能和語法都擴展很多。首先說一下枚舉的定義。 枚舉聲明的類型是囊括可能狀態...
    正直走閱讀 296評論 0 0
  • 鑒于昨天開會部門會議討論的時候,發現有些朋友對枚舉的用法還是存在一些疑問,所以就寫下這個文章,介紹下Swift下的...
    AndreaArlex閱讀 52,964評論 14 68
  • Swift中不僅提供了 面向過程的編程支持,也提供了全面的面向對象的支持,與普通的面向對象的編程語言(只支持類)不...
    隨夢而飛飛閱讀 5,182評論 3 4