列舉的基本用法
enum Direction {
case north
case south
case east
case west
}
// 簡便寫法
enum Direction {
case north, south, east, west
}
var dir = Direction.west
dir = Direction.east
dir = .north
switch dir {
case .north:
print("north")
case .south:
print("south")
case .east:
print("east")
case .west:
print("west")
}
關聯值(Associated Values)
有時會將列舉的成員值和其他型別的值關聯存盤在一起,會非常有用
enum Score {
case points(Int)
case grade(Character)
}
var score = Score.points(96)
score = .grade("A")
switch score {
case let .points(i):
debugPrint(i)
case let .grade(i):
debugPrint(i)
}
enum Date {
case digit(year: Int, month: Int, day: Int)
case string(String)
}
var date = Date.digit(year: 2020, month: 12, day: 5)
date = .string("2020-12-5")
switch date {
case .digit(let year, let month, let day):
debugPrint(year, month, day)
case let .string(value):
debugPrint(value)
}
enum Password {
case number(Int, Int, Int, Int)
case gesture(String)
}
var pwd = Password.number(5, 6, 4, 7)
pwd = .gesture("12369")
switch pwd {
case let .number(n1, n2, n3, n4):
print("number is ", n1, n2, n3, n4)
case let .gesture(str):
print("gesture is ", str)
}
必要時let可以改成var
原始值(Raw Values)
列舉成員可以使用相同型別的默認值預先關聯,這個默認值叫做原始值
enum PokerSuit: String {
case spade = "1"
case heart = "2"
case diamond = "3"
case club = "4"
}
let suit = PokerSuit.heart
debugPrint(suit)
debugPrint(suit.rawValue)
debugPrint(PokerSuit.spade.rawValue)
原始值不占用列舉變數的記憶體,原始值只是關聯上了列舉變數,所以原始值占用記憶體的大小并不是列舉變數的大小
隱式原始值(Implicitly Assigned Raw Values)
如果列舉的原始值型別是Int、String,Swift會自動分配原始值
字串默認分配的原始值就是其變數名
enum Direction: String {
case north = "north"
case south = "south"
case east = "east"
case west = "west"
}
// 等價于上面
enum Direction: String {
case north, south, east, west
}
debugPrint(Direction.north.rawValue) // north
Int型別默認分配的原始值是從0開始遞增的數字
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 = 4, winter
}
print(Season.spring.rawValue) // 1
print(Season.summer.rawValue) // 2
print(Season.autumn.rawValue) // 4
print(Season.winter.rawValue) // 5
遞回列舉(Recursive Enumeration)
遞回列舉要用indirect關鍵字來修飾enum,不然會報錯
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 sum = ArithExpr.sum(five, four)
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獲取資料型別占用的記憶體大小
64bit的Int型別占8個位元組
let age = 10
MemoryLayout<Int>.stride // 8, 分配占用的空間大小
MemoryLayout<Int>.size // 8, 實際用到的空間大小
MemoryLayout<Int>.alignment // 8, 對齊引數
等同于
MemoryLayout.size(ofValue: age)
MemoryLayout.stride(ofValue: age)
MemoryLayout.alignment(ofValue: age)
關聯值和原始值的區別:關聯值型別會存盤到列舉變數里面,原始值因為一開始就會知道默認值是多少,所以只做記錄,不會存盤
enum Password {
case number(Int, Int, Int, Int)
case other
}
MemoryLayout<Password>.stride // 40,分配占用的空間大小
MemoryLayout<Password>.size // 33,實際用到的空間大小
MemoryLayout<Password>.alignment // 8,對齊引數
enum Session: Int {
case spring, summer, autnmn, winter
}
MemoryLayout<Session>.stride // 1,分配占用的空間大小
MemoryLayout<Session>.size // 1,實際用到的空間大小
MemoryLayout<Session>.alignment // 1,對齊引數
列舉變數的記憶體布局
我們知道通過MemoryLayout可以獲取到列舉占用記憶體的大小,那列舉變數分別占用多少記憶體呢?
要想知道列舉變數的大小,我們需要通過查看列舉變數的記憶體布局來進行分析
列舉變數的分析準備
我們可以需要通過Xcode里的View Memory選項來查看詳細的記憶體布局
1.可以在運行程式時,通過控制臺列印的列舉變數右鍵選擇View Memory of *進入到記憶體布局的頁面

2.還可以從Xcode標題欄中選擇Debug -> Debug Workflow -> View Memory進入到記憶體布局的頁面

3.進入到該頁面,然后通過輸入變數的記憶體地址來查看

4.我們可以下載一個小工具來獲取到變數的記憶體地址
下載地址:https://github.com/CoderMJLee/Mems
5.然后將下載好的這個檔案Mems.swift拖到自己的Xcode中
呼叫這個函式就可以了
print(Mems.ptr(ofVal: &t))
我們來分析下面的列舉變數的情況
enum TestEnum {
case test1, test2, test3
}
var t = TestEnum.test1
print(Mems.ptr(ofVal: &t))
t = TestEnum.test2
t = TestEnum.test3
print(MemoryLayout<TestEnum>.stride) // 1
print(MemoryLayout<TestEnum>.size) // 1
print(MemoryLayout<TestEnum>.alignment) // 1
分別將斷點打在給列舉變數t賦值的三句代碼上,然后運行程式觀察每次斷點之后的記憶體布局有什么變化



通過上圖可以看到,每個列舉變數都分配了一個位元組的大小,并且存盤的值分別是0、1、2,我們可以知道這一個位元組的大小就是用來存盤列舉成員值的
我們再來分析一個列舉變數的情況
enum TestEnum: Int {
case test1 = 1, test2 = 2, test3 = 3
}
var t = TestEnum.test1
print(Mems.ptr(ofVal: &t))
t = TestEnum.test2
t = TestEnum.test3
print(MemoryLayout<TestEnum>.stride) // 1
print(MemoryLayout<TestEnum>.size) // 1
print(MemoryLayout<TestEnum>.alignment) // 1



通過上圖可以看到,每個列舉變數存盤的值也是0、1、2,并且分配了一個位元組的大小
可以證明列舉變數的記憶體大小和原始值型別無關,而且列舉變數里存盤的值和原始值也無關
我們再來分析一個列舉變數的情況
enum TestEnum {
case test1(Int, Int, Int) // 24
case test2(Int, Int) // 16
case test3(Int) // 8
case test4(Bool) // 1
case test5 // 1
}
var t = TestEnum.test1(1, 2, 3)
print(Mems.ptr(ofVal: &t))
t = TestEnum.test2(4, 5)
t = TestEnum.test3(6)
t = TestEnum.test4(true)
t = TestEnum.test5
MemoryLayout<TestEnum>.size // 25
MemoryLayout<TestEnum>.stride // 32
MemoryLayout<TestEnum>.alignment // 8
我們先通過列印了解到列舉型別總共分配了32個位元組,然后我們通過斷點分別來觀察列舉變數的記憶體布局


執行完第一句我們可以看到,前面24個位元組分別用來存盤關聯值1、2、3,第25個位元組用來存盤成員值0,之所以分配32個位元組是因為記憶體對齊的原因
// 調整排版后的記憶體布局如下所示
01 00 00 00 00 00 00 00
02 00 00 00 00 00 00 00
03 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00


執行完第二句我們可以看到,前面16個位元組分半用來存盤關聯值4、5,然后第25個位元組用來存盤成員值1
// 調整排版后的記憶體布局如下所示
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 00 00 00 00 00 00 00


執行完第三句我們可以看到,前面8個位元組分半用來存盤關聯值6,然后第25個位元組用來存盤成員值2
// 調整排版后的記憶體布局如下所示
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


執行完第四句我們可以看到,由于是Bool型別,那么只用了第一個位元組來存盤關聯值1,然后第25個位元組用來存盤成員值3
// 調整排版后的記憶體布局如下所示
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


執行完最后一句我們可以看到,由于沒有關聯值,那么只用了第25個位元組存盤成員值4
// 調整排版后的記憶體布局如下所示
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
總結:記憶體分配情況:一個位元組存盤成員值,n個位元組存盤關聯值(n取占用記憶體最大的關聯值),任何一個case的關聯值都共有這n個位元組
我們再來看幾個情況
enum TestEnum {
case test
}
MemoryLayout<Session>.stride // 1,分配占用的空間大小
MemoryLayout<Session>.size // 0,實際用到的空間大小
MemoryLayout<Session>.alignment // 1,對齊引數
如果列舉里只有一個case,那么實際用到的空間為0,都不用特別分配記憶體來進行存盤
enum TestEnum {
case test(Int)
}
MemoryLayout<Session>.stride // 8,分配占用的空間大小
MemoryLayout<Session>.size // 8,實際用到的空間大小
MemoryLayout<Session>.alignment // 8,對齊引數
可以看到分配的記憶體大小就是關聯值型別決定的,因為只有一個case,所以都不需要再額外分配記憶體來存盤是哪個case了
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/271194.html
標籤:iOS
上一篇:iOS-審核4.3入坑(已出坑)
下一篇:Swift 進階(四)結構體和類
