Swift枚舉

枚舉為一組相關的值定義了一個共同的類型,使你可以在你的代碼中以類型安全的方式來使用這些值。
我們熟悉 的C 語言,枚舉會為一組整型值分配相關聯的名稱。Swift 中的枚舉更加靈活,不必給每一個枚舉成員提供一個值。如果給枚舉成員提供一個值(稱為原始值),則該值的類型可以是字符串字符,或是一個整型值浮點數
此外,枚舉成員可以指定任意類型的關聯值存儲到枚舉成員中,就像其他語言中的聯合體(unions)和變體(variants)。你可以在一個枚舉中定義一組相關的枚舉成員,每一個枚舉成員都可以有適當類型的關聯值
Swift 中,枚舉類型是一等(first-class)類型。它們采用了很多在傳統上只被類(class)所支持的特性,例如計算屬性(computed properties),用于提供枚舉值的附加信息;實例方法(instance methods),用于提供和枚舉值相關聯的功能;枚舉也可以定義構造函數(initializers)來提供一個初始值;可以在原始實現的基礎上擴展它們的功能;還可以遵循協議(protocols)來提供標準的功能。

一、枚舉語法

Swift 中,枚舉支持IntDoubleString等基礎類型,也有默認枚舉值String類型默認枚舉值為casekey名稱,IntDouble數值型默認枚舉值為0開始,+1遞增。
代碼:

// 寫法一
// 不需要逗號隔開
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


/*
String類型的enum
- =左邊的值是枚舉值,例如 MON
- =右邊的值在swift中稱為 RawValue(原始值),例如 "MON"
- 兩者的關系為:case 枚舉值 = rawValue原始值
*/
enum Week: String{
    case MON = "MON"
    case TUE = "TUE"
    case WED = "WED"
    case THU = "THU"
    case FRI = "FRI"
    case SAT = "SAT"
    case SUN = "SUN"
}

如果不想寫枚舉值后的字符串,也可以使用隱式RawValue分配。

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

//打印結果:MON
1.2 枚舉的訪問

那么Swift是如何獲取rawValue的值,我們可以通過SIL文件分析。
SIL命令:

swiftc -emit-sil main.swift |xcrun swift-demangle >> ./main.sil && open main.sil

SIL文件

enum Week : String {
  case MON, TUE, WED, THU, FRI, SAT, SUN
  init?(rawValue: String)      //默認添加了一個可選類型的init方法
  typealias RawValue = String  //給枚舉值的類型,通過typealias取了一個別名RawValue
  var rawValue: String { get } //增加一個計算屬性rawValue,用于獲取枚舉值的原始值
}

main函數流程:

image.png

rawValuegetter方法:
image.png

bb8代碼段:
image.png

總結:rawValue的底層就是調用的getter方法,getter方法中構造了字符串,但是這個字符串的值(例如“MON”)從哪里取出的呢?他們在編譯期已經確定了,我們查看Mach-O,在TEXT的__cstring段就能看到。

1.3 case枚舉值 & rawValue原始值

代碼:

//輸出 case枚舉值
print(Week.MON)
//輸出 rawValue
print(Week.MON.rawValue)
//打印結果:MON  MON

從結果來看是沒有什么區別的,輸出值都一樣,但他們本質是不一樣的。
第一個,輸出的case枚舉值;第二個,是通過rawValue訪問的rawValue的get方法
下面這種寫法,編譯器就會報錯:

image.png

1.4 枚舉的init

枚舉的init會在什么時候調用,我們通過斷點看一下:

image.png

①,不設置Condition,進不到方法里面;②原生代碼第一個斷點,進入的是getter方法,進不去init
即,enuminit方法的調用是通過枚舉.init(rawValue:)或者枚舉(rawValue:)觸發的。

繼續:

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

//打印結果:
//Optional(_6_EnumTest.Week.MON)
//nil

第一個輸出的可選值,第二個輸出的是nil。表示,沒有找到對應的case枚舉值
分析SIL文件中的Week.init方法,主要有以下幾步:
1、在init方法中是將所有enum的字符串從Mach-O文件中取出,依次放入數組中;
2、放完后,然后調用_findStringSwitchCase方法進行匹配。
如下:

2251862-4a1f18106a9065f7_.jpg

index_addr:表示獲取當前數組中的第n個元素值的地址,然后再把構建好的字符串放到當前地址中
struct_extract:表示取出當前的Int值,Int類型在系統中也是結構體
cond_br:表示比較的表達式,即分支條件跳轉
    - 如果匹配成功,則構建一個 .some的Optional 返回
    - 如果匹配不成功,則繼續匹配,知道最后還是沒有匹配上,則構建一個.none的Optional返回

_findStringSwitchCaseswift-source中,接收兩個參數,分別是 數組 + 需要匹配的String。①遍歷數組,如果匹配則返回對應的index;②如果不匹配,則返回-1
所以,這也是為什么一個打印可選值,一個打印nil的原因。

1.5枚舉的遍歷

CaseIterable協議通常用于沒有關聯值的枚舉,用來訪問所有的枚舉值,只需要對應的枚舉遵守該協議即可,然后通過allCases獲取所有枚舉值,如下:

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

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

二、關聯值枚舉、模式匹配、屬性方法

如果希望用枚舉表示復雜的含義,關聯更多的信息,就需要使用關聯值了。

他與普通類型的枚舉不同沒有rawValue,沒有rawValue的getter方法;沒有初始化init方法

//注:當使用了關聯值后,就沒有RawValue了
//因為:case可以用一組值來表示,而rawValue是單個的值
enum Shape{
    //case枚舉值后括號內的就是關聯值,如 radius
    case circle(radius: Double)
    case rectangle(width: Int, height: Int)
}

//創建
var circle = Shape.circle(radius: 10.0)

//重新分配
circle = Shape.rectangle(width: 10, height: 10)
模式匹配

enum中的模式匹配其實就是匹配case枚舉值,根據枚舉類型,分為2種:
1、簡單類型的枚舉的模式匹配;
2、自定義類型的枚舉(關聯值)的模式匹配。

簡單enum的模式匹配

注:swift中的enum模式匹配需要將所有情況都列舉,或者使用default表示默認情況,否則會報錯

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

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

//打印結果:unknow day
關聯值類型的模式匹配

關聯值類型模式匹配有兩種方式:1、switch - case, 匹配所有case;2、if - case, 匹配單個case
switch - case
定義關聯值枚舉:

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

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

let shape = Shape.circle(radius: 10.0)
switch shape{
    //相當于將10.0賦值給了聲明的radius常量
    case let .circle(radius):
        print("circle radius: \(radius)")
    case .rectangle(let width, var height):
        height += 1
        print("rectangle width: \(width) height: \(height)")
}
  • 通過if-case匹配單個case,如下:
let circle = Shape.circle(radius: 10)

//匹配單個case
if case let Shape.circle(radius) = circle {
    print("circle radius: \(radius)")
}
  • 如果我們只關心不同case的相同關聯值(即關心不同case的某一個值),需要使用同一個參數
    例如,案例中的x,如果分別使用x、y, 編譯器會報錯:
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:
        break
}

也可以使用通配符_(表示匹配一切)的方式:

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

注:枚舉使用過程中不關心某一個關聯值,可以使用通配符_表示。OC只能調用swift中Int類型的枚舉。

屬性 & 函數

enum中可以包含計算屬性類型屬性不能包含存儲屬性

enum Direct: Int {
    case up
    case down
    case left
    case right
    
    // 計算型屬性
    var description: String{
        switch self {
        case .up:
            return "這是上面"
        default:
            return "這是\(self)"
        }
    }
    
    //存儲屬性:編譯器報錯,Enums must not contain stored properties
    //var radius: Double
    
    //類型屬性 - 是一個全局變量
    static let height = 20.0
    
    // 函數
    func printSelf() {
        print(description)
    }
    
    mutating func nextDay(){
        if self == .up{
            self = Direct(rawValue: 1)!
        }else{
            self = Direct(rawValue: self.rawValue+1)!
        }
    }
}

Direct.down.printSelf()
//打印結果:這是down

var direct = Direct.left;
direct.nextDay();
direct.printSelf()
//打印結果:這是right

為什么struct中可以放存儲屬性,而enum不可以?
struct中可以包含存儲屬性,是因為其大小就是存儲屬性的大小。而enum是不一樣的(請查閱后文的enum大小講解),enum枚舉的大小是取決于case的個數的,如果沒有超過255,enum的大小就是1字節(8位)

可以在enum中定義實例方法static修飾的方法

enum Week: Int{
    case MON, TUE, WED, THU, FRI, SAT, SUN
    
    mutating func nextDay(){
        if self == .SUN{
            self = Week(rawValue: 0)!
        }else{
            self = Week(rawValue: self.rawValue+1)!
        }
    }
}

<!--使用-->
var w = Week.MON
w.nextDay()
print(w)

三、枚舉的嵌套

枚舉的嵌套主要用于以下場景:
1、枚舉嵌套枚舉:一個復雜枚舉是由一個或多個枚舉組成;
2、結構體嵌套枚舉:enum是不對外公開的,即是私有的。

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)
結構體嵌套枚舉
//結構體嵌套枚舉
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")
        }
    }
}
枚舉的遞歸:indirect

遞歸枚舉是一種枚舉類型,它有一個或多個枚舉成員使用該枚舉類型的實例作為關聯值。使用遞歸枚舉時,編譯器會插入一個間接層。你可以在枚舉成員前加上 indirect 來表示該成員可遞歸。

//一、用枚舉表示鏈表結構
enum List<T>{
    case end
    //表示case使是引用來存儲
    indirect case node(T, next: List<T>)
}


//二、也可以將indirect放在enum前
//表示整個enum是用引用來存儲
indirect enum List<T>{
    case end
    case node(T, next: List<T>)
}

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

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

print(MemoryLayout<Int>.size)
print(MemoryLayout<List<Int>>.size)
print(MemoryLayout<List<Int>>.stride)

print(MemoryLayout<String>.size)
print(MemoryLayout<List<String>>.size)
print(MemoryLayout<List<String>>.stride)

//打印結果:
//8  8  8
//16  8  8

發現IntString都是8。為什么?
下面通過LLDB分析查看一下:

image.png

如果是end,此時存儲的是case值,為0,而case為node時存儲的是引用地址
所以,indirect關鍵字其實就是通知編譯器,我當前的enum是遞歸的,大小是不確定的,需要分配一塊堆區的內存空間,用來存放enum。

四、swift和OC混編enum

在swift中,enum非常強大,而在OC中,enum僅僅只是一個整數值
因此,OC調用Swift枚舉,必須具備2個條件:①、用@objc關鍵字標記enum;②、當前enum應該是Int類型

// Swift中定義枚舉
@objc enum Weak: Int{
    case MON, TUE, WED, THU, FRI, SAT, SUN
}

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

Swift使用OC枚舉,OC中的枚舉會自動轉換成swift中的enum

// OC定義1:NS_ENUM
NS_ENUM(NSInteger, ENUM_OC_TYPE){
    Value1,
    Value2
};

// OC定義2:typedef enum
typedef enum {
    Num1,
    Num2
}OCEnumType;

// swift使用
//1、將OC頭文件導入橋接文件xxx-Bridging-Header.h
#import "xxx.h"
//2、使用
let ocEnum1 = ENUM_OC_TYPE.Value1
let ocEnum2 = OCEnumType.init(0)

print("\(ocEnum1)  +  \(ocEnum2.rawValue)")
//打印結果:ENUM_OC_TYPE  +  0

OC自動轉換成swift方式:
image.png

image.png

上圖可知,通過typedef enum定義的enum,在swift中變成了一個結構體,并遵循了兩個協議:EquatableRawRepresentable
OC使用Swift中String類型的枚舉方式

@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

即:swift中的enum成Int整型;enum再聲明一個變量/方法,用于返回固定的字符串,用于在swift中使用。

五、Enum內存大小

我們主要分析兩個函數的區別:

  1. size:實際占用內存大小;
  2. stride:系統分配的內存大小。
5.1 普通enum

一個case的情況:

enum Weak {
    case MON
    //case TUE
}

print(MemoryLayout<Weak>.size)
print(MemoryLayout<Weak>.stride)
//打印結果:0  1

再加一個case:

enum Weak {
    case MON
    case TUE
}

print(MemoryLayout<Weak>.size)
print(MemoryLayout<Weak>.stride)
//打印結果:1  1

繼續增加多個case:

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

print(MemoryLayout<Weak>.size)
print(MemoryLayout<Weak>.stride)
//打印結果:1  1

以上可以看出,當case個數為1時,枚舉size為0個數>=2時,size的大小始終是1,即,說明enum就是以1字節存儲在內存中的。why?

image.png

讀取內存可以看出,case都是1字節大小,1個字節是8個byte,那么有255種排列組合(0x00000000 - 0x11111111)。
所以,當case為1個的時候,size的大小是0(二進制是0x0)case數<=255時,size都是1,是UInt8類型
超過255個時,會自動擴容,size和stride都會增加

總結:
1、如果enum中有原始值,即rawValue,其大小取決于case的多少,如果沒有超過UInt8即255,則就是1字節存儲case,Int標識的其實就是 RawValue的值。
2、當只有一個case的情況下,size是0,表示這個enum是沒有意義的。
3、當有兩個及以上case時,如果沒有超過255,則case的步長是1字節;如果超過,則UInt8->UInt16...,以此類推。

5.2 關聯值的enum

自定義類型的枚舉,即關聯值類型,sizestride值的變化,如下:

enum Shape{
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
}
print(MemoryLayout<Shape>.size)
print(MemoryLayout<Shape>.stride)
//打印結果:17   24

分析:

  1. case(枚舉值)size1
  2. 枚舉是共用內存,有關聯值時,取最大值
    2.1 circle的參數是double類型,內存size8
    2.2 rectangle的參數是兩個double類型,內存size16
  3. 最大值,為16。即,內存size為:16(最大關聯值大小) + 1(case枚舉值)= 17
  4. 內存對齊,所以stride為24
    斷點讀取內存驗證一下:
    image.png
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)
//打印結果:2  2

說明:enum嵌套enum同具有關聯值的enum是一樣的,同樣取決于關聯值的大小,其內存大小是最大關聯值的大小
定義一個變量,觀察一下內存:

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

總結,有待進一步驗證:
enum嵌套enum同樣取決于最大case的關聯值大小;
當嵌套enum的case只有2個時,case在內存中的存儲是0、8;
當嵌套enum的case大于2,小于等于4時,case在內存中的存儲是 0、4、8、12;
當嵌套enum的case大于4時,case在內存中的存儲是從0、1、2...以此類推。

5.4 結構體嵌套enum
struct Skill {
    enum KeyType{
        case up
        case down
        case left
        case right
    }
}
print(MemoryLayout<Skill>.size)
print(MemoryLayout<Skill>.stride)
//打印結果:0   1

如果只嵌套了enum,沒有聲明變量。size的大小取決于成員變量,但是struct中目前沒有屬性,所以size是1

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)
//打印結果:1  1

結構體的大小計算,跟函數無關,所以只看成員變量key的大小,key是枚舉Skill類型,大小為1,所以結構體大小為1
如果在添加一個成員變量:

struct Skill {
    enum KeyType{
        case up
        case down
        case left
        case right
    }
    let key: KeyType //1字節
    var height: UInt8 //1字節

    func launchSkill(){
        switch key {
        case .left, .right:
            print("left, right")
        case .up, .down:
            print("up, down")
        }
    }
}
print(MemoryLayout<Skill>.size)
print(MemoryLayout<Skill>.stride)
//打印結果:2   2

在添加一個Int屬性:

struct Skill {
    enum KeyType{
        case up
        case down
        case left
        case right
    }
    let key: KeyType //1字節
    var height: UInt8 //1字節
    var width: Int //8字節

    func launchSkill(){
        switch key {
        case .left, .right:
            print("left, right")
        case .up, .down:
            print("up, down")
        }
    }
}
print(MemoryLayout<Skill>.size)
print(MemoryLayout<Skill>.stride)
//打印結果:16   16
//打印結果2:如果把Int屬性放在最前面,則為,10   16

為什么屬性位置不一樣,打印結果不一樣,也是因為內存對齊。內存對齊規則如下:

/**
 數據成員的對齊規則可以理解為min(m, n) 的公式
 m,表示當前成員的開始位置;n,表示當前成員所需要的位數。
 如果滿足條件 m 整除 n (即 m % n == 0), n 從 m 位置開始存儲;
 反之繼續檢查 m+1 能否整除 n, 直到可以整除, 從而就確定了當前成員的開始位置。
 */
struct Mystruct4{
    int a;              //4字節 min(0,4)--- (0,1,2,3)
    struct Mystruct5{   //從4開始,存儲開始位置必須是最大的整數倍(最大成員為8),min(4,8)不符合 4,5,6,7,8 -- min(8,8)滿足,從8開始存儲
        double b;       //8字節 min(8,8)  --- (8,9,10,11,12,13,14,15)
        short c;         //1字節,從16開始,min(16,1) -- (16,17)
    }Mystruct5;
}Mystruct4;
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        NSLog(@"%lu  %lu", sizeof(Mystruct4), sizeof(Mystruct4.Mystruct5));
        //打印結果:24  16
        
        NSLog(@"Hello, World!");
    }
    return 0;
}

結構體嵌套了enum總結:
1、如果沒有聲明變量,此時的size是0,stride是1
2、如果結構體中沒有其他屬性,只有枚舉變量,那么結構體的大小就是枚舉的大小,即size為1
3、如果結構體中還有其他屬性,則按照OC中的結構體內存對齊原則進行分析。

內存對齊 & 字節對齊 區分
內存對齊:iOS中是8字節對齊,蘋果實際分配采用16字節對齊,這種只會在分配對象時出現。
字節對齊:存儲屬性的位置必須是偶地址,即OC內存對齊中的min(m,n),其中m表示存儲的位置n表示屬性的大小,需要滿足位置m整除n時,才能從該位置存放屬性。簡單來說,就是必須在自身的倍數位置開始。
外部調用對象時,對象是服從內存對齊
單純從結構上說,結構內部服從最大字節對齊。即,枚舉size為1的情況。

總結:
一、枚舉定義:
1、 enum中使用rawValue的本質是調用get方法,即在get方法中從Mach-O對應地址中取出字符串并返回的操作;
2、 enuminit方法的調用是通過枚舉.init(rawValue:)或者枚舉(rawValue:)觸發的;
3、沒有關聯值的enum,如果希望獲取所有枚舉值,需要遵循CaseIterable協議,然后通過枚舉名.allCase的方式獲取;
4、case枚舉值rawValue原始值的關系:case 枚舉值 = rawValue原始值
5、具有關聯值的枚舉,可以稱為三無enum,因為沒有別名RawValue、init、計算屬性rawValue
6、enum模式匹配方式,主要有兩種:switch-case / if-case
7、enum可以嵌套enum,也可以在結構體中嵌套enum,表示該enum是struct私有的;
8、enum中還可以包含計算屬性類型屬性,但是不能包含存儲屬性
9、enum中可以定義實例static修飾的方法
二、枚舉內存:
1、普通enum的內存大小一般是1字節,如果只有一個case,則為0,表示沒有意義,如果case個數超過255,則枚舉值的類型由UInt8->UInt16->UInt32...;
2、具有關聯值的enum大小,取決于最大case的內存大小+case的大小(1字節)
3、enum嵌套enum同樣取決于最大case的關聯值大小
4、結構體嵌套enum,如果沒有屬性,則size為0,如果只有enum屬性size為1,如果還有其他屬性,則按照OC中內存對齊原則進行計算。

參考:枚舉教程

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

推薦閱讀更多精彩內容

  • Swift 枚舉(enum)詳解 [TOC] 本文將介紹Swift中枚舉的一些用法和其底層原理的一些探索,以及探索...
    just東東閱讀 16,542評論 6 22
  • 前言 本篇文章將講述Swift中很常用的也很重要的一個知識點 ?? Enum枚舉。首先會介紹與OC中枚舉的差別,接著...
    深圳_你要的昵稱閱讀 2,507評論 0 5
  • 與C,Objective-C中的枚舉相比,Swift中枚舉功能更強大。它支持很多只有類才有的特性,如:Proper...
    YY323閱讀 676評論 0 6
  • C語言的枚舉 C語言的枚舉寫法 我們通過枚舉表示一周的七天 c語言中,枚舉的第一個成員默認是為0,后面的枚舉值一次...
    浪的出名閱讀 404評論 0 1
  • 枚舉的遍歷 合成枚舉 allCases Swift4.2引入 protocol CaseIterable,它被用于...
    呂建雄閱讀 1,073評論 0 1