Swift之枚舉

本文首發於個人我的博客git

枚舉

枚舉的基本用法

定義

//定義方向的枚舉
enum Direction {
     case north
     case south
	 case east
	 case west 
}
複製代碼

上面也能夠寫成github

enum Direction {
    case north, south, east, west
}
複製代碼

使用

var dir = Direction.west 
dir = Direction.east 
dir = .north
print(dir) // north
複製代碼

也能夠在switch中使用spring

switch dir { 
case .north:
	print("north") 
case .south:
    print("south") 
case .east:
	print("east") 
case .west:
    print("west")
}
複製代碼

關聯值

有時將枚舉的成員值跟其餘類型的值關聯存儲在一塊兒,會很是有用,能夠認爲將值直接存入到枚舉的內存中

enum Score {
    case points(Int)
    case grade(Character)
}
複製代碼

以下使用:編程

var score = Score.points(88) 
score = .grade("A")

複製代碼

若是咱們想使用枚舉的具體值,能夠以下用 i 來保存數據swift

switch score {
	case let .points(i):
		print(i, "points") 
	case let .grade(i):
    	print("grade", i)
} // grade A

複製代碼

再好比咱們想定義日期的枚舉值,能夠以下:bash

enum Date {
    case digit(year: Int, month: Int, day: Int)
    case string(String)
}

//使用的時候,能夠直接傳年月日,或者傳字符串

var date = Date.digit(year: 2011, month: 9, day: 10) 
date = .string("2011-09-10")
switch date {
	case .digit(let year, let month, let day):
			rint(year, month, day) 
	case let .string(value):
    	print(value)
}

複製代碼

必要時let也能夠改成var

原始值

枚舉成員可使用相同類型的默認值預先對應,這個默認值叫作:原始值

// 定義枚舉
enum Grade : String {
    case perfect = "A"
    case great = "B"
    case good = "C"
    case bad = "D"
}

// 使用
print(Grade.perfect.rawValue) // A 
print(Grade.great.rawValue) // B 
print(Grade.good.rawValue) // C 
print(Grade.bad.rawValue) // D

複製代碼

注意:原始值不佔用枚舉變量的內存

隱式原始值(Implicitly Assigned Raw Values)

若是枚舉的原始值類型是Int、String,Swift會自動分配原始值

原始值是 String 類型枚舉值

// 定義枚舉值
  enum Direction : String {
    case north = "north"
    case south = "south"
    case east = "east"
    case west = "west"
}
// 等價於
enum Direction : String {
    case north, south, east, west
}
// 使用
print(Direction.north) // north 
print(Direction.north.rawValue) // north

複製代碼

原始值是 Int 類型枚舉值

enum Season : Int {
    case spring, summer, autumn, winter
}
print(Season.spring.rawValue) // 0 
print(Season.summer.rawValue) // 1 
print(Season.autumn.rawValue) // 2 
print(Season.winter.rawValue) // 3

複製代碼

若是本身指定了原始值

enum Season : Int {
    case spring = 1, summer, autumn = 8, winter
}
print(Season.spring.rawValue) // 1 
print(Season.summer.rawValue) // 2 
print(Season.autumn.rawValue) // 8 
print(Season.winter.rawValue) // 9

複製代碼

遞歸枚舉

遞歸枚舉要加上關鍵字 indirect

eg:app

// 遞歸枚舉
indirect enum ArithExpr {
	case number(Int)
	case sum(ArithExpr, ArithExpr)
	case difference(ArithExpr, ArithExpr)
}

// 上面的遞歸枚舉和下面的等效

enum ArithExpr {
	case number(Int)
	indirect case sum(ArithExpr, ArithExpr) 
	indirect case difference(ArithExpr, ArithExpr)
}

// 下列幾種使用枚舉都是能夠的

let five = ArithExpr.number(5)
let four = ArithExpr.number(4)
let two = ArithExpr.number(2)
let sum = ArithExpr.sum(five, four)
let difference = ArithExpr.difference(sum, two)


// 本身寫個 calculate 方法,
func calculate(_ expr: ArithExpr) -> Int { 
	switch expr {
	case let .number(value): 
		return value
	case let .sum(left, right):
		return calculate(left) + calculate(right)
	case let .difference(left, right):
		return calculate(left) - calculate(right)
} }

//最終調用,計算差值
calculate(difference)
複製代碼

MemoryLayout

可使用MemoryLayout獲取數據類型佔用的內存大小

關聯值

  • 將關聯值直接存入到枚舉內存中
// 定義枚舉
 enum Password {
    case number(Int, Int, Int, Int)
    case other
}

MemoryLayout<Password>.stride // 40, 分配佔用的空間大小 
MemoryLayout<Password>.size // 33, 實際用到的空間大小 4*8 + 1 = 33
MemoryLayout<Password>.alignment // 8, 對齊參數
複製代碼

定義變量來使用ide

var pwd = Password.number(9, 8, 6, 4)

MemoryLayout.stride(ofValue: pwd) // 40
MemoryLayout.size(ofValue: pwd) // 33 
MemoryLayout.alignment(ofValue: pwd) // 8
 
// 若是改變了pwd
 pwd = .other 
MemoryLayout.stride(ofValue: pwd) // 40
MemoryLayout.size(ofValue: pwd) // 33 
MemoryLayout.alignment(ofValue: pwd) // 8

複製代碼

原始值

  • 原始值不會直接存入到枚舉內存中
  • 若是是下面這種枚舉,只須要1個字節就能夠了
  • 一個字節能夠存放 FF 也就是 0~255個枚舉值。若是
enum Season : Int {
    case spring, summer, autumn = 8, winter
}

MemoryLayout<Season>.stride // 1, 分配佔用的空間大小 
MemoryLayout<Season>.size // 1, 實際用到的空間大小 1
MemoryLayout<Season>.alignment // 1, 對齊參數

複製代碼

窺探內存

使用 窺探內存細節的小工具 咱們能夠很輕鬆的獲取swift中,這些枚舉值的內存地址工具

簡單枚舉內存

enum TestEnum{
    case k0,k1,k2,k3
}

var t = TestEnum.k1
print(Mems.ptr(ofVal: &t)) 

t = TestEnum.k2

複製代碼

執行完 print(Mems.ptr(ofVal: &t)) 代碼以後 輸出佈局

0x00000001000054b8
複製代碼

此時去查看 0x00000001000054b8地址的數據,

01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
複製代碼

執行完 t = TestEnum.k2 以後

此時去查看 0x00000001000054b8地址的數據,

02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
複製代碼

具體查看內存

假設咱們有這麼下面帶代碼,考慮下內存怎麼佈局呢?

enum TestEnum {
        case test1(Int, Int, Int)
        case test2(Int, Int)
        case test3(Int)
        case test4(Bool)
        case test5
}

print(MemoryLayout<TestEnum>.size)  // 25, 分配佔用的空間大小
print(MemoryLayout<TestEnum>.stride)    //32, 實際用到的空間大小
print(MemoryLayout<TestEnum>.alignment)// 8, 對齊參數


var e = TestEnum.test1(1, 2, 3)
print(Mems.ptr(ofVal: &e))
  
  
e = .test2(4, 5)
print(Mems.memStr(ofVal: &e))
    
e = .test3(6)
     
e = .test4(true)
      
e = .test5
複製代碼

執行完以後,可知

TestEnum 這個佔用內存爲:

  • print(MemoryLayout.size) // 25, 分配佔用的空間大小
  • print(MemoryLayout.stride) //32, 實際用到的空間大小
  • print(MemoryLayout.alignment)// 8, 對齊參數

具體內存裏面存的是什麼呢?能夠藉助上面說的 窺探內存細節的小工具 打印出來內存,而後利用Xcode的 view Memory 查看具體內存的值

結果以下

enum TestEnum {
        case test1(Int, Int, Int)
        case test2(Int, Int)
        case test3(Int)
        case test4(Bool)
        case test5
    }
    
    
    print(MemoryLayout<TestEnum>.size)  // 25, 分配佔用的空間大小
    print(MemoryLayout<TestEnum>.stride)    //32, 實際用到的空間大小
    print(MemoryLayout<TestEnum>.alignment)// 8, 對齊參數

    // 1個字節存儲成員值
    // N個字節存儲關聯值(N取佔用內存最大的關聯值),任何一個case的關聯值都共用這N個字節
    // 共用體
    
    // 小端:高高低低
    // 01 00 00 00 00 00 00 00  //對應的TestEnum.test1傳入的數值
    // 02 00 00 00 00 00 00 00
    // 03 00 00 00 00 00 00 00
    // 00                // TestEnum.test1在第0個位置
    // 00 00 00 00 00 00 00
    var e = TestEnum.test1(1, 2, 3)
    print(Mems.ptr(ofVal: &e))
    
    // 04 00 00 00 00 00 00 00
    // 05 00 00 00 00 00 00 00
    // 00 00 00 00 00 00 00 00
    // 01						 // TestEnum.test1在第1個位置
    // 00 00 00 00 00 00 00
    e = .test2(4, 5)
    print(Mems.memStr(ofVal: &e))
    
    // 06 00 00 00 00 00 00 00
    // 00 00 00 00 00 00 00 00
    // 00 00 00 00 00 00 00 00
    // 02
    // 00 00 00 00 00 00 00
    e = .test3(6)
    
    // 01 00 00 00 00 00 00 00
    // 00 00 00 00 00 00 00 00
    // 00 00 00 00 00 00 00 00
    // 03
    // 00 00 00 00 00 00 00
    e = .test4(true)
    
    // 00 00 00 00 00 00 00 00
    // 00 00 00 00 00 00 00 00
    // 00 00 00 00 00 00 00 00
    // 04
    // 00 00 00 00 00 00 00
    e = .test5
複製代碼

只有一個case

假如只有一個case,其佔用的空間爲0,不須要存值來區分是哪一個case

enum TestEnum {
    case spring
}

print(MemoryLayout<TestEnum>.size)      // 0, 分配佔用的空間大小
print(MemoryLayout<TestEnum>.stride)    //1, 實際用到的空間大小
print(MemoryLayout<TestEnum>.alignment)// 1, 對齊參數

複製代碼

只有一個case,有關聯值

enum TestEnum {
    case spring(Int)
}

print(MemoryLayout<TestEnum>.size)      // 8, 分配佔用的空間大小
print(MemoryLayout<TestEnum>.stride)    //8, 實際用到的空間大小
print(MemoryLayout<TestEnum>.alignment)// 8, 對齊參數

複製代碼

參考資料:

Swift官方源碼

從入門到精通Swift編程

窺探內存細節的小工具

更多資料,歡迎關注我的公衆號,不定時分享各類技術文章。

相關文章
相關標籤/搜索