本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗漏或未能一一列举原文出处还望见谅与指出,另文章内容如有不妥之处还望指教,万分感谢。
枚举
枚举的基本用法
enum Direction {
case north
case south
case east
case west
}
-------------两段代码等价--------------
enum Direction {
case north, south, east, west
}
- 关联值(Associated Values)
- 有时会将枚举的
成员值
跟其他类型
的值关联存储在一起
,会非常有用
特点:关联值可以随意修改,枚举所占内存大小跟关联值有直接关系
Int : 关联值类型是Int
(Int,Int,Int,Int):关联值
enum PokerSuit : Int {
case spade(Int,Int,Int,Int)
case other
}
po: 枚举变量
var po = PokerSuit. spade(11,22,33,44)
- 原始值(Raw Values)
枚举成员可以使用相同类型
的默认值
预先关联,这个默认值叫做:原始值
特点:原始值是固定的,不可修改;不会占用枚举变量内存
, 因为它不存储在枚举变量内存里
Character : 原始值类型是字符类型
"♠":原始值
enum PokerSuit : Character {
case spade = "♠"
case heart = "红"
}
po: 枚举变量
var po = PokerSuit. spade
这里的rawValue就是获取枚举成员的原始值
var rawValue = 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
}
字符串的名字是什么,默认关联的值也就是这个字符串
Int类型关联值会是:0、1、2、3
- 递归枚举 (Recursive Enumeration), 枚举前需要定义
indirect
关键字
indirect enum ArithExpr {
case number(Int)
case sum(ArithExpr, ArithExpr)
case differ(ArithExpr, ArithExpr)
}
---------------两段代码等同----------------
enum ArithExpr {
case number(Int)
indirect case sum(ArithExpr, ArithExpr)
indirect case differ(ArithExpr, ArithExpr)
}
- MemoryLayout
- 可以使用
MemoryLayout
获取数据类型占用的内存大小
获取Int类型在当前架构(arm64)实际使用内存大小
MemoryLayout<Int>.size
获取Int类型在当前架构(arm64)分配的内存大小
MemoryLayout<Int>.stride
内存对齐参数
MemoryLayout<Int>.alignment
var age = 20
MemoryLayout.size(ofValue: age)
MemoryLayout.stride(ofValue: age)
MemoryLayout.alignment(ofValue: age)
可选项
- 可选项,一般也叫做可选类型;特点:它允许将值设置为
nil
,普通的可变类型初始化之后,赋值为nil
编译器会直接报错 - 多应用在不确定情况的场景
- 在类型名称后面加一个问好
?
来定义一个可选项 - 本质:可选项是对其他类型的一层包装,可以将它理解为一个盒子
- 如果为
nil
, 那么它是个空盒子,反之盒子里包装的是:被包装的类型数据
var name: String? = "逍遥侯"
name = nil
------------------------------------
var age:Int? //默认就是nil
age = 10
age = nil
------------------------------------
利用可选类型适配数组越界
var list = [1,2,4,9]
func get(_ index: Int) ->Int?
{
if index < 0 || index >= list.count {
return nil
}
return list[index]
}
print(get(1)) //Optional(15)
print(get(-1)) //nil
print(get(4)) //nil
强制解包(Forced Unwrapping)
如果要从可选项中取出被包装的数据(将盒子里包装的东西取出来),需要使用感叹号 !进行强制解包
强制解包值是访问盒子中的值,不会对盒子有影响
如果对值为
nil
的可选项进行强制解包,会产生运行时错误
var age:Int? //默认就是nil
age = 10
var hhage: Int = age!
hhage += 10
输出:20
- 判断可选项是否包含值
//字符串转Int,可能转换成功(整数),也可能转换失败(nil)
let number = Int("224") //number是 number?,即可选类型;
//转换失败number=nil, 反之成功
if number != nil {
print("字符串转换整数成功:\(number!)") //强制解包
} else {
print("字符串转换整数失败")
}
- 可选项绑定(Optional Binding)
- 可以使用
可选项绑定
来判断可选项是否包含值- 如果包含就自动解包,把值赋给一个临时的常量(
let
)或者变量(var
),并返回ture
,否则返回false
- 如果包含就自动解包,把值赋给一个临时的常量(
//先将字符串强制转换为Int ,如果成功返回一个包含224的可选项,并自动解包,最终会把224这个数值赋值给number;条件判断返回ture ; 反之返回false
if let number = Int("224") {
print("字符串转换整数成功:\(number!)") //强制解包
} else {
print("字符串转换整数成功")
}
- 可选项绑定
等价写法
: 使用逗号(,
)隔开; 不能够使用&&
- while循环中使用可选项绑定
示例:
遍历数组,将遇到的正数都加起来,如果遇到负数或者非数字,则停止遍历返回
var strs = ["10","13","18","abj","-2","19","0",]
var index = 0
var sum = 0
可选项绑定,且num大于零; 逗号表示两个条件要同时成立
while let num = Int(strs[index]), num > 0 {
sum += num
index += 1
}
print(sum)
- 空合运算符: ?? (Nil - Coalescing Operator)
空合运算符源码定义
public func ?? <T>(optional: T?, defaultValue: @autoclosure () thorws - > T?) rethrows - > T?
public func ?? <T>(optional: T?, defaultValue: @autoclosure () thorws - > T) rethrows - > T?
简单用法:
a
?? b
a : 要求必须是可选项;如果不是编译器会报⚠️,不建议这么写
b和a的存储类型必须相同
b : 是可选项 或者 不是可选项
b是可选项:
如果a不为nil
,就返回 a
如果a为nil
,就返回 b
b不是可选项 :
如果a不为nil
,就返回a盒子中的值; 且返回a时会自动解包
如果a为nil
,就返回b
结论:空合运算返回的类型取决于b的类型,b是什么类型最终就返回什么类型
- 多个
??
一起使用; 规则从左到右运算
示例:
let a: Int? = 1
let b: Int? = 2
let c = a ?? b ?? 3
分析: 首先会先运算a??b, 因为a不为空且b是可选类型,所以返回可选类型a, 然后 a??3 , a不为空,3不是可选类型,最终解包a盒子中的值1;
所以c是Int类型 值为1
-
??
跟if let
配合使用
获取两个可选类型中有值得哪一个
let a: Int? = nil
let b: Int? = 2
if let c = a ?? b {
print(c)
}
//类似于 if a != nil || b != nil
-----------------------------
c绑定a,a不等于空,且 d绑定b,b不等于空
if let c = a , let d = b {
print(c)
print(d)
}
//类似于if a != nil && b != nil
-
guard
语句,当要使用多次if
判断时,可以考虑是用guard
代替
注意:做完事情必须退出作用域,可以使用关键字:return
、break
、continue
、throw
、error
- 当
guard
语句的条件为false
时,就会执行大括号里面的代码 ? - 当
guard
语句的条件为true
时,就会跳过guard语句 -
guard
语句特别适合用来实现”提前退出
“ - 当使用
guard
语句进行可选项绑定
时,绑定的常量、变量也能在外层作用域中使用
如下示例中username
和password
可以在外层作用域访问
- 隐式解包 (Implicitly Unwrapped Optional)
- 在某些情况下,可选项一旦被设定值之后,就会一直拥有值
- 在这种情况下,可以去掉检查,也不一定每次访问的时候都进行解包,因为它能确定每次访问的时候都有值
- 可以在类型后面加个感叹号
!
定义一个隐式解包的可选项 - 感叹号和问好都代表可选类型,只不过问号需要强制解包后才能拿到盒子里的值,而感叹号已经自动对可选类型进行了解包,可以直接使用
隐式解包的可选项
let num: Int != 10
let num1: Int = num
- 字符串插值或空合运算符,消除打印可选项的⚠️
- 多重可选项,可以是一个问号
?
、两个问号??
、 三个问号???
- 可以使用lldb指令
frame variable -R
或者fr v -R
查看区别
包装了一个Int类型的可选类型
var num1: Int? = 10
包装了一个可选类型的可选类型,这里的??不代表空合运算,需要注意
var num2: Int?? = num1
包装了Int类型可选类型的可选类型, 这样写就等同于num2
var num3: Int?? = 30
包装了空的可选类型
var num1: Int? =nil
包装了一个空的可选类型的可选类型,这里的??不代表空合运算,需要注意
var num2: Int?? = num1
包装了大的空可选类型
var num3: Int?? = nil