- 作者: Liwx
- 郵箱: 1032282633@qq.com
- 源碼: 需要
源碼
的同學, 可以在評論區
留下您的郵箱
iOS Swift 語法
底層原理
與內存管理
分析 專題:【iOS Swift5語法】00 - 匯編
01 - 基礎語法
02 - 流程控制
03 - 函數
04 - 枚舉
05 - 可選項
06 - 結構體和類
07 - 閉包
08 - 屬性
09 - 方法
10 - 下標
11 - 繼承
12 - 初始化器init
13 - 可選項
目錄
- 01-枚舉的基本用法
- 02-關聯值(Associated Values)
- 03-關聯值舉例
- 04-原始值(Raw Values)
- 05-隱式原始值(Implicitly Assigned Raw Values)
- 06-遞歸枚舉(Recursive Enumeration)
- 07-內存布局(MemoryLayout)
01-枚舉的基本用法
// 寫法1
enum Direction {
case north
case south
case east
case west
}
// 寫法2
enum Direction {
case north, south, east, west
}
var dir = Direction.west
dir = Direction.east
dir = .north
print(dir) // north
switch dir {
case .north:
print("north") // north
case .south:
print("south")
case .east:
print("east")
case .west:
print("west")
}
02-關聯值(Associated Values)
-
關聯值
- 將枚舉的
成員值
跟其他類型
的值關聯存儲
在一起
- 將枚舉的
enum Score {
case points(Int)
case grade(Character)
}
var score = Score.points(96)
score = .grade("A")
switch score {
case let .points(i):
print(i, "points")
case let .grade(i):
print("grade", i)
} // grade A
- 必要時
let
也可以改為var
enum Date {
case digit(year: Int, month: Int, day: Int) // 可使用標簽定義
case string(String)
}
var date = Date.digit(year: 2020, month: 1, day: 2)
date = .string("2020-01-03")
switch date {
case .digit(var year, let month, let day): // year var
print(year, month, day)
case let .string(value):
print(value)
} // 2020-01-03
03-關聯值舉例
enum Password {
case number(Int, Int, Int, Int)
case gesture(String)
}
var pwd = Password.number(3, 5, 7, 8)
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)
}
04-原始值(Raw Values)
- 原始值
-
枚舉成員
可以使用相同類型的默認值
與之對應, 這個默認值叫做:原始值
- 注意:
原始值不占用枚舉變量的內存
-
// PokerSuit的原始值類型為Character
enum PokerSuit : Character {
case spade = "?"
case heart = "?"
case diamond = "??"
case club = "??"
}
var suit = PokerSuit.spade
print(suit) // spade
print(suit.rawValue) // ?
print(PokerSuit.club.rawValue) // ??
// Grade的原始值類型為String
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
05-隱式原始值(Implicitly Assigned Raw Values)
如果枚舉的
原始值類型
是Int
、String
, Swift會自動分配原始值
String
類型自動分配原始值
// 寫法1
enum Direction : String {
case north = "north"
case south = "south"
case east = "east"
case west = "west"
}
// 寫法2 等價于寫法1
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
-
Int
類型自定義原始值
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
06-遞歸枚舉(Recursive Enumeration)
-
遞歸枚舉
定義關鍵字indirect
- 遞歸枚舉即枚舉成員
存在遞歸調用
的枚舉類型
- 遞歸枚舉即枚舉成員
- 枚舉
內部成員添加indirect
關鍵字, 在調用本身的內部成員前加indirect
enum ArithExpr {
case number(Int)
indirect case sum(ArithExpr, ArithExpr)
indirect case difference(ArithExpr, ArithExpr)
}
- 枚舉
外部添加indirect
關鍵字
indirect enum ArithExpr {
case number(Int)
case sum(ArithExpr, ArithExpr)
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)
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) // 7
07-內存布局(MemoryLayout)
- 使用
MemoryLayout
獲取數據類型占用的內存大小
enum Password {
case num(Int, Int, Int, Int)
case other
}
MemoryLayout<Password>.stride // 40, 分配占用的空間大小
MemoryLayout<Password>.size // 33, 實際用的空間大小
MemoryLayout<Password>.alignment// 8, 內存對齊
var pwd = Password.num(9, 8, 6, 4)
pwd = .other
MemoryLayout.stride(ofValue: pwd)// 40, 分配占用的空間大小
MemoryLayout.size(ofValue: pwd) // 33, 實際用的空間大小
MemoryLayout.alignment(ofValue: pwd)// 8, 內存對齊
iOS內存布局:
小端
-
查看內存方式
- 查看內存方式1: 底部
終端左側
窗口,選擇要查看的變量/常量,右鍵菜單-View Memory of "變量/常量名"
- 查看內存方式2:
Debug - DebugWorkflow - View Memory
, 快捷鍵control+option+commond+shift+M
放開后再按enter
鍵 - 使用
Mems
工具打印內存地址 GitHub鏈接
- 查看內存方式1: 底部
- 簡單
查看常量/變量內存
布局
var a = 10
print(a) // 這邊打斷點觀察
// Int型變量a內存分配8字節
// 0A 00 00 00 00 00 00 00
image.png
- 查看
枚舉內存布局
enum TestEnum {
case test1, test2, test3
}
var t = TestEnum.test1 // 內存: 00
t = .test2 // 內存: 01
t = .test3 // 內存: 02
print(Mems.ptr(ofVal: &t)) // 內存: 02
print(MemoryLayout<TestEnum>.size) // 這邊打斷點觀察 1
print(MemoryLayout<TestEnum>.stride) // 1
print(MemoryLayout<TestEnum>.alignment) // 1
image.png
- 枚舉
原始值內存查看
- 枚舉類型后面
冒號 : 后面的類型
表示原始值
類型 -
原始值不影響
枚舉變量內存存儲
- 枚舉類型后面
enum TestEnum1 : Int { // 原始值為Int
case test1 = 1, test2 = 2, test3 = 3
}
var t1 = TestEnum1.test1 // 內存: 00
print(Mems.ptr(ofVal: &t1))
t1 = .test2 // 01
t1 = .test3 // 02
// - 原始值不影響枚舉變量內存存儲
print(MemoryLayout<TestEnum1>.size) // 1 斷點觀察
print(MemoryLayout<TestEnum1>.stride) // 1
print(MemoryLayout<TestEnum1>.alignment) // 1
image.png
- 枚舉
關聯值內存
查看-
1個
字節存儲成員值
(索引值) -
N個
字節存儲關聯值
(N: 占用內存最大的關聯值
),任何一個case的關聯值都共用這N個字節
-
enum TestEnum2 {
case test1(Int, Int, Int)
case test2(Int, Int)
case test3(Int)
case test4(Bool)
case test5
}
print(MemoryLayout<TestEnum2>.size) // 25
print(MemoryLayout<TestEnum2>.stride) // 32
print(MemoryLayout<TestEnum2>.alignment) // 8
var t2 = TestEnum2.test1(1, 2, 3)
print(Mems.ptr(ofVal: &t2))
// 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// 成員值,類似索引值
// Mems.memStr內存里面的內容
print(Mems.memStr(ofVal: &t2))
// 0x0000000000000001 0x0000000000000002 0x0000000000000003 0x0000000000000000
print(Mems.memStr(ofVal: &t2, alignment: .one))
// 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
t2 = .test2(4, 5)
// 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// 成員值,類似索引值
t2 = .test3(6)
// 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// 成員值,類似索引值
t2 = .test4(true)
// 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// 成員值,類似索引值
t2 = .test5
// 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// 成員值,類似索引值
print(t2)
- 如果枚舉
只有一個成員值
, 實際沒有用到內存
enum TestEnum3 {
case test
}
var t3 = TestEnum3.test
print(Mems.ptr(ofVal: &t3)) // 0x0000000000000001
print(t3)
print(MemoryLayout<TestEnum3>.size) // 0 實際沒有用到內存
print(MemoryLayout<TestEnum3>.stride) // 1
print(MemoryLayout<TestEnum3>.alignment) // 1
- 枚舉
只有一個關聯值的成員值
內存查看-
無需1個
字節存儲成員值
-
enum TestEnum4 {
case test(Int)
}
var t4 = TestEnum4.test(10)
print(Mems.ptr(ofVal: &t4))
print(t4)
print(MemoryLayout<TestEnum4>.size) // 8
print(MemoryLayout<TestEnum4>.stride) // 8
print(MemoryLayout<TestEnum4>.alignment) // 8
- 枚舉的switch語句底層是如何實現的?
- 通過枚舉的
成員值
(類似索引值)來判斷要執行哪個case
enum TestEnum5 {
case test1(Int, Int, Int)
case test2(Int, Int)
case test3(Int)
case test4(Bool)
case test5
}
// - 通過枚舉的成員值(類似索引值)來判斷要執行哪個case
// - TestEnum5.test2(10, 20) 僅僅只是內存賦值操作, 不存在函數調用
var t5 = TestEnum5.test2(10, 20)
switch t5 {
case let .test1(v1, v2, v3):
print("test1", v1, v2, v3)
case let .test2(v1, v2):
print("test2", v1, v2)
case let .test3(v1):
print("test3", v1)
case let .test4(v1):
print("test4", v1)
case let .test5:
print("test5")
} // test2 10 20
image.png
iOS Swift 語法
底層原理
與內存管理
分析 專題:【iOS Swift5語法】