- 可选项,也叫做可选类型。定义为可选类型后,可以设置为
nil
- 在类型名后面加个问号
?
来定义可选类型 - 可选类型如果不初始化,则默认为
nil
var name : String? /*等同于*/ var name : String? = nil
1. 强制解包
- 可选项是对其它类型的一种包装,可以将它理解为一个盒子
- 如果为
nil
,那么它就是一个空盒子 - 如果不为
nil
,那么盒子里包装的是:被包装类型的数据
var name : String? = nil
let arr = ["jack","uzi","godV","rookie"]
func getObj(index:Int) -> String?{
if index<0 || index >= arr.count {
return nil
}else{
return arr[index]
}
}
print(name)
print(getObj(index: 3))
/*
nil
Optional("rookie")
*/
- 如果要从可选项中取出被包装的数据,则需要使用感叹号 !进行强制解包
- 如果对值为
nil
的可选项进行强制解包,将会产生运行时错误
错误信息:Fatal error: Unexpectedly found nil while unwrapping an Optional value
2. 可选绑定
- 可以使用
可选项绑定
来判断可选项是否包含值 - 如果包含就自动解包,把值赋给一个临时的常量 或者 变量,并返回true,否则返回 false
let num = Int("123")
print(num)//Optional(123)
if let num = Int("123"){
print("num:",num)//num: 123
}else{
print("字符串转换失败")
}
3. if 中的可选绑定
可选项在进行 与(&&) 运算时。不能直接使用 &&,可以用逗号分隔的方式
如下:
if let a = Int("12") {
if let b = Int("44") {
if a < b && b < 100 {
print("\(a) < \(b) < 100")
}
}
}
//等同于
if let a = Int("12"),
let b = Int("44"),
a < b && b < 100{
print("\(a) < \(b) < 100")
}
3. while 中的可选绑定
//遍历数组,进行累加,如果遇到非数字,停止遍历
var strs = ["1","2","-3","3","4","5",]
var index:Int = 0
var sum:Int = 0
while
index < strs.count,
let obj = Int(strs[index]){
sum += obj
index += 1
}
print("sum:",sum)//12
4 ?? 空合并运算符
示例:a ?? b
a:必须是可选项
b:可选项 或者 不是可选项
b跟a的存储类型必须相同
- 如果 a 不为 nil,则返回 a
- 如果 a 为 nil, 则返回b
- 如果b不是可选项,返回 a 时,则a会自动解包
let a: Int? = 1
let b: Int? = 2
let c: Int = 3
let d: Int? = nil
print(a ?? b, a ?? c, d ?? b) //Optional(1) 1 Optional(2)
4.1 ?? 跟if let 配合使用
- 类似于 if v1 != nil || v2 != nil
let v1:Int? = nil
let v2:Int? = 2
if let c = v1 ?? v2{
print(c)
}
//类似于 if v1 != nil || v2 != nil
- 类似于 if v1 != nil && v2 != nil
let v1:Int? = nil
let v2:Int? = 2
if let c = v1,
let d = v2{
print("result_c:",c)
print("result_d:",d)
}
5. guard 语句
guard 条件 else{
//退出当前作用域
}
- 当
guard
语句的条件为false
时, 就会执行大括号里的代码 - 当
guard
语句的条件为true
时,就会调过guard
-
guard
语句特别适合用来"提前退出"
注意:
当使用guard
语句进行可选绑定时,可选绑定的常量(let
)、变量(var
)也能在外层作用域中使用
func login(_ info:[String : String]) -> Bool {
guard let username = info["username"] else{
print("用户名不能为空")
return false
}
guard let password = info["password"] else{
print("用户密码不能为空")
return false
}
print("登录中 username:\(username) password:\(password)")
return false
}
login(["password" : "12345678"])//用户名不能为空
login(["username" : "Jack"])//用户密码不能为空
login(["username" : "Jack", "password" : "12345678"])//登录中 username:Jack password:12345678
对比使用 if-else
更加简便
func login2(_ info:[String : String]) -> Bool {
let username:String
if let tempUsername = info["username"] {
username = tempUsername
}else{
print("用户名不能为空")
return false
}
let password:String
if let tempPassword = info["password"] {
password = tempPassword
}else{
print("用户密码不能为空")
return false
}
print("登录中 username:\(username) password:\(password)")
return true
}
6. 隐式解包
- 在某些情况下,可选项一旦诶设定后,就会一直拥有值
- 这种情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能够确定每次访问的时候都有值
- 可以在类型后面加感叹号!定义一个隐式解包的可选项, 其值不能为
nil
let num1 :Int! = 1 //虽然是隐式解包,但其仍是可选项
let num2 :Int = num1 //隐式解包可以直接赋值给Int类型
if let k = num1 {
print(k)
}else{
print("nil")
}
7. 多重可选项
结构分析
注意:
可以使用lldb指令:frame variable -R
或者 fr v -R
查看区别
序号 | 情况1 | 情况2 |
---|---|---|
1 | var n1 : Int? = 10 | var n1 : Int? = nil |
2 | var n2 : Int?? = n1 | var n2 : Int?? = n1 |
3 | var n3 : Int?? = 10 | var n3 : Int?? = nil |
对于情况1:
var n1 : Int? = 10
var n2 : Int?? = n1
var n3 : Int?? = 10
对于情况2:
var n1 : Int? = nil
var n2 : Int?? = n1
var n3 : Int?? = nil
分析:个人觉得
1. 可选类型 some/none {...}中的内容即为盒子里的内容。
2. none 标识的是空盒子,当为空盒子时。下层{}也就不存在意义
3. 对于情况2中 n1 n3都为空盒子;n2不是空盒子,盒子里有内容,内容为空