开始今天的内容之前先补充个关键字:
@inline
@inline
内敛函数
如果开启了编译器优化(Release模式默认开启优化),编译器会自动将某些函数变成内敛函数.
哪些函数不会被自动内敛?
1、代码比较长
2、递归调用函数
3、动态派发函数
可以通过never、__always关键字控制一下内敛:
- 永远不会被内敛(即便开启了编译器优化)
@inline(never) func test() {
print("test")
}
- 开启编译器优化后,即使代码比较长,也会被内敛(递归调用函数、动态派发函数除外)
@inline(__always) func test() {
print("test")
}
release环境下,编译器已经开启优化,会自动决定哪些函数需要内敛,因此没必要使用@inline
枚举的基本用法
回顾一下以前的C语言下枚举的本质就是整型(0、1、2 ...)
enum Score {
case one
case two
case three
}
let e = Score.one
关联值(Associated Values)
案例1
var score = Score.point(96)
score = Score.grade("A")
switch score {
case let .point(i):
print(i,"points")
case let .grade(i):
print("grade", i)
}
案例2
enum Date {
case digit(year: Int, month: Int, day: Int)
case string(String)
}
var date = Date.digit(year: 2020, month: 6, day: 26)
date = Date.string("2020-06-26")
switch date {
case .digit(year: let year, month: let month, day: let day):
print(year, month, day)
case let .string(value)
print(value)
}
结论,关联值将各种类型的数据直接存储在枚举变量里面去。
原始值(Raw Values)
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)//♣️
隐式原始值(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
}
print(Direction.north) // north
print(Direction.north.rawValue) // north
再比如:
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)
实例1: 算术表达式
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)
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 difference(left) - difference(right)
}
}
calculate(difference)
MemoryLayout
可以使用MemoryLayout
获取数据类型占用的内存大小(在OC里面有个sizeOf)
MemoryLayout是支持泛型
的.
实例1
enum Password {
case number(Int, Int, Int, Int)
case other
}
var pwd = Password.number(1,2,3,4) // 占用多少个字节?
pwd = .other // 这个呢?
MemoryLayout<Password>.stride//40, 分配占用的空间大小
MemoryLayout<Password>.size//33, 实际用到的空间大小
MemoryLayout<Password>.alignment//8
enum Season {
case spring, summer, autumn, winter
}
MemoryLayout<Season>.stride//1
MemoryLayout<Season>.size//1
MemoryLayout<Season>.alignment//1
var age = 10
MemoryLayout.stride(ofValue: age)// 8
MemoryLayout.size(ofValue: age) // 8
MemoryLayout.alignment(ofValue: age)// 8
这种类型的枚举
enum Season {
case spring, summer, autumn, winter
}
var s = Season.spring// 1个字节
//原始值是Int
enum Season : Int {
case spring = 1, summer = 2, autumn = 3, winter = 4
}
var s = Season.spring// 1个字节
var s1 = Season.summer// 1个字节
var s2 = Season.autumn// 1个字节
//原始值是String
enum Season : String {
//序号 0 1 2 3
case spring = "1", summer = "2", autumn = "3", winter = "3"
}
var s = Season.spring// 1个字节
var s1 = Season.summer// 1个字节
var s2 = Season.autumn// 1个字节
思考1:Apple对原始值是怎么实现的?
思考2:上面例子Password实际用到的空间大小为什么实33?32不行吗?
关联值、原始值的区别?
关联值把传进来的关联的值存放到枚举变量里面
关联值的特点:可以传入不同的值,意味着每一个枚举变量都要有内存来存储不同的值.
var pwd1 = Password.number(10,999,10000,10000)
原始值和枚举成员绑定在一起,固定不变的一个值.所以没有必要给它分配内存空间放到枚举变量里面
var s = Season.spring// 1个字节
var s1 = Season.summer// 1个字节
var s2 = Season.autumn// 1个字节
猜测:默认值可以这样实现
enum Season3: Int {
case spring = 1, summer, autumn, winter
func rawValue() {
if self == .spring return 1
else if self == .summer return 2
else if self == .autumn return 3
...
}
}
可选项(Optional)
类型后面加?
var name: String? = "Jack"
name = nil
var age: Int? // 默认值为nil
age = 10
age = nil
等价
var age: Int?
var age: Int? = nil
不等价
var age: Int
var age: Int = 0
强制解包(Forced Unwarpping)
可选项是对其他类型的包装
var age1: Int?// 把Int类型的数据添加到盒子里面
age1 = 10
var age2 = age1! + 2
对nil强制解包会carsh
实例
var num = "abc123"
var numInt = Int(num)
if numInt != nil {
print("字符串转换成功\(numInt!)")
} else {
print("字符串转换失败")
}
可选项绑定(Optional Binding)
实例1
if let number = Int("123") {
print("字符串转换成功\(numInt!)")
} else {
print("字符串转换失败")
}
实例2
if let season = Season(rawValue: 6) {
} else {}
while循环中使用可选项绑定
let strs = ["10","10","abc","5","10","10",]
var index = 0
var sum = 0
while let num = Int(strs[index]), num > 0 {
sum+=num
index+=1
}
空合并运算符??
源码如下
func ?? <T>(optional: T?, defaultValue: @autoclosure() throws -> T?) throws -> T?
func ?? <T>(optional: T?, defaultValue: @autoclosure() throws -> T) throws -> T
a??b
1、a是可选项
2、b是可选项或者不是可选项
3、b跟a存储类型必须相同
4、如果a不为nil,返回a;如果a为nil,返回b
5、如果b不是可选项,返回a时会自动解包,
实例1
let a: Int? = 1
let b: Int? = 2
let c = a ?? b // c是Int?, Optional(1)
实例2
let a: Int? = nil
let b: Int? = 2
let c = a ?? b // c是Int?, Optional(2)
实例3
let a: Int? = nil
let b: Int? = nil
let c = a ?? b // c是Int?, nil
实例4
var a: Int? = nil
var b:Int = 1
let c = a ?? b // c是Int, 1
使用场景:
var a: Int? = nil
var b:Int = 2
// 如果不使用??运算符
let c: Int
if let temp = a {
c = temp
} else {
c = b
}
多个??一起使用
实例1
var a: Int? = 1
var b: Int? = 2
let c = a ?? b ?? 3 // c是Int, 1
实例2
var a: Int? = nil
var b: Int? = 2
let c = a ?? b ?? 3 // c是Int, 2
实例3
var a: Int? = nil
var b: Int? = nil
let c = a ?? b ?? 3 // c是Int, 3
注意⚠️
var a: Int? = nil
var b: Int? = nil
let c = a ?? 2 ?? b // 这样写是会警告的,想想为什么?
??跟if let配合使用
var a: Int? = nil
var b: Int? = 2
if let c = a ?? b {
print(c)
}
类似于: if a!=nil || b != nil
使用场景: 如果有多个可选项,其中有一个可选项不为nil,就可以进来.
var a: Int? = nil
var b: Int? = 2
if let c = a, let d = b {
print(c)
}
类似于: if a!=nil && b != nil
if 语句实现登录
看例子前先补充一下知识点:
- swift里面通过key从字典里面取出来的value是可选类型的.
- 数组里面通过下标取出来的是真实类型(所以数组是否越界需要我们自己判断呀)
func login(_ info: [String: String]) {
let userName: String
if let temp = info["username"] {
userName = temp
} else {
print("请输入用户名")
return
}
let pwd: String
if let temp = info["password"] {
pwd = temp
} else {
print("请输入密码")
return
}
print("用户名:\(userName),密码:\(pwd)登录中")
}
guard语句
当guard语句的条件为false
时,就会执行大括号里面的代码;
当guard语句的条件为true
时,就会跳出guard语句;
guard语句特别适合用来提前退出
.
guard 条件 else {
// do something
退出当前作用域
// return、break 、continue 、throw error
}
当guard语句进行可选绑定时,绑定的常量(let)、变量(var)也能在外层作用域使用
func login1(_ info: [String: String]) {
guard let userName = info["username"] else {
print("请输入用户名")
return
}
guard let pwd = info["password"] else {
print("请输入密码")
return
}
print("用户名:\(userName),密码:\(pwd)登录中")
}
隐私解包(Implicitly Unwrapped Optional)
在某些情况下,可选值一旦被设定值之后,就会一直拥有值,
在这种情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值.
可以在类型后面加一个感叹号!定义一个隐式解包的可选项
以前写法
let num1: Int? = 10
let num2: Int = num1!
隐私解包的可选项
var num1: Int! = 10
var num2: Int = num1
无论是Int!还是Int?都是可选类型
var num1: Int! = 10
var num2: Int = num1
if num1 != nil {
print(num1 + 6)
}
if let num3 = num1 {
print(num3)
}
let num1: Int! = nil
//Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
let num2: Int = num1
感觉这种类型没啥用?有木有
使用场景:如果能确保一定有值可以使用
希望别人给你值,不希望别人给你nil,但是别人可能给你的是nil.
多重可选项
var num1: Int?
var num2: Int?? = num1
var num3: Int?? = 10
frame variable -R
简写:fr v -R 变量名 查看变量结构