- 作者: 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语法】
网友评论