//可选项 Optional
//可选项,一般也叫可选类型,它允许将值设置为nil,默认不允许设置为nil
//在类型名称后面加个问号? 来定义一个可选项
var str: String? = "123"
str = nil
var age: Int? //可选值默认就是nil,var age:Int等价于var age:Int?= nil
age = 10
age = nil
var array = [1, 15, 40, 29]
func get(_ index:Int) -> Int? { //如果函数返回值可能为nil,返回值类型就要加?
if index < 0 || index >= array.count {
return nil
} else {
return array[index]
}
}
print(get(1)) //Optional(15)
print(get(-1))//nil
print(get(4)) //nil
var age1:Int = 15
print(age1) //15
var age2:Int? = 15
print(age2) //Optional(15), 这个是可选类型
//强制解包(Forced Unwrapping)
//可选项是对其他类型的一层包装,可以将它理解为一个盒子
//如果为nil,那么它是个空盒子
//如果不为nil,那么盒子里面装的是:被包装类型的数据
var age3: Int? //默认就是nil
age3 = 10
age3 = nil
把10赋值给age3,如图相当于Int10这个蓝色的框框去掉
//如果要从可选项中取出被包装的数据(将盒子里面装的东西取出来),需要使用!进行强制解包
var age4: Int? = 10
var ageInt: Int = age4!
ageInt += 10
var age5: Int? = 10
var num = age5! + 20 //30
print(age5) //Optional(10)
//强制解包只是取值的过程,并不会修改age5本身的可选项值
//如果对值为nil的可选项(空盒子)进行强制解包,将会产生运行时错误,crash奔溃
//报错信息:Fatal error: Unexpectedly found nil while unwrapping an Optional value
var age6: Int?
//age6! //这里会crash,报错先注释掉了
//判断可选项是否包含值
let number = Int("123")
if number != nil {
print("字符串转换成功:\(number!)") //这里的number可能转换失败,返回0或者-1都不合适,只能返回nil,所以这种通过字符串转换成Int类型,number是可选类型,需要强制解包
} else {
print("字符串转换整数失败,返回nil")
}
// 字符串转换整数成功:123
//var num = Int("xxx")
//可选项绑定
//可以使用可选项绑定来判断可选项是否包含值
//如果包含就自动解包,把值赋给一个临时的常量(let)或者变量(var),并返回true,否则返回false
if let number = Int("123") {
print("字符串转换整数成功:\(number)")
// number是强制解包之后的Int值
// number作用域仅限于这个大括号
} else {
print("字符串转换整数失败")
}
enum Season: Int {
case spring = 1, summber, autumn, winter
}
//var sea = Season(rawValue: 2) 这里的sea是可选项,因为获取枚举传入原始值,可能不存在对应原始值的枚举变量
if let season = Season(rawValue: 6) { //这里需要注意,可选项绑定会帮忙自动解包,获取到的直接就是Season类型,而不是Season?可选类型
switch season {
case .spring:
print("the season is spring")
default:
print("the season is other")
}
} else {
print("no such season")
}
//no such season
// 等价写法
if let first = Int("4") {
if let second = Int("42") {
if first < second && second < 100 {
print("\(first) < \(second) < 100")
}
}
}
//上面的写法等价于下面的写法:
if let first = Int("4"), //这里的可选项绑定,必须要使用, 逗号隔开,不能直接使用&&符合拼接
let second = Int("42"),
first < second && second < 100 {
}
//while循环中使用可选项绑定
//遍历数组,将遇到的正数都加起来,如果遇到负数或者非数字,停止遍历
var strs = ["10", "20", "30", "abc", "-20", "30"]
var index = 0
var sum = 0
while let num = Int(strs[index]), num > 0 {
sum += num
index += 1
}
print(sum) //60 10+20+30=60
//空合并运算符 ?? (Nil-Coalescing Operator)
//public func ?? <T>(optional:T?, defaultValue:@autoclosure () throws -> T?) rethrows -> T?
//
//public func ?? <T>(optional:T?, defaultValue:@autoclosure () throws -> T) rethrows -> T
//a ?? b
//1. 其中a必须是可选项,
//2. b是可选项 或者不是可选项
//3. b和a的存储类型必须相同
//4. 如果a 不为nil,就返回a
//5. 如果a 为nil,就返回b
//6. 如果b 不是可选项,返回a时自动解包
let a: Int? = 1
let b: Int? = 2
let c = a ?? b //1
let a1: Int? = nil
let b1: Int? = 2
let c1 = a1 ?? b1 //2
let a2: Int? = nil
let b2: Int? = nil
let c2 = a2 ?? b2 //nil
let a3: Int? = 1
let b3: Int = 2
let c3 = a3 ?? b3 //c是Int, 1
let a4: Int? = nil
let b4: Int = 2
let c4 = a4 ?? b4 //2
let a5: Int? = nil
let b5: Int = 2
//如果不使用?? 运算符
let c5: Int
if let tmp = a5 { //可选项会绑定失败
c5 = tmp
} else {
c5 = b5
}
//多个??一起使用
let a6: Int? = 1
let b6: Int? = 2
let c6 = a6 ?? b6 ?? 3 //c是Int,1
let a7: Int? = nil
let b7: Int? = 2
let c7 = a7 ?? b7 ?? 3 //c7是Int,2
let a8: Int? = nil
let b8: Int? = nil
let c8 = a8 ?? b8 ?? 3 //c8是Int,3
let a9: Int? = 1
let b9: Int? = 2
let c9 = a9 ?? 3 ?? b //c9是Int,这种会警告,??左边只能是可选项,不可以是Int
//??跟if let配合使用
let a10: Int? = nil
let b10: Int? = 2
if let c10 = a10 ?? b10{
print(c10)
}
//类似于 if a != nil || b != nil, 并且可以自动解包赋值给 c10
//类似这种会有多个可选项解包可以使用多个 ?? 连接起来使用
if let c = a, let d = b {
print(c)
print(d)
}
//类似于 if a != nil && b != nil
//a自动解包成功并且b自动解包成功,if条件才满足,且的关系
//if 语句实现登陆
func login(_ info: [String: String]) {
var username: String
if let tmp = info["username"] {
username = tmp
} else {
username = ""
print("请输入用户名")
return
}
var password: String
if let tmp = info["password"] {
password = tmp
} else {
password = ""
print("请输入密码")
return
}
//if username...
//if password...
print("用户名:\(username)","密码:\(password)","登陆ing")
}
login(["username":"jack","password":"123456"]) //用户名:jack 密码:123456 登陆ing
login(["password":"123456"]) //请输入用户名
login(["username":"jack"]) //请输入密码
//guard 语句
//当guard 语句的条件为false时,就会执行大括号里面的代码
//当guard 语句为true时,就会跳过guard语句
//guard语句特别适合用来 “提前退出”
func test() {
guard 1>2 else {
//do something
//一定要推出当前作用域
return
}
}
//当使用guard语句进行可选项绑定时,绑定的常量(let),变量(var)也能在外层作用域中使用
//前面登陆的代码可以用guard来优化下:
func login2(_ info:[String:String]) {
guard let username = info["username"] else {
print("请输入用户名")
return
}
guard let password = info["password"] else {
print("请输入密码")
return
}
//if username...
//if password...
print("用户名:\(username)","密码:\(password)","登陆ing")
}
login2(["username":"jack","password":"123456"]) //用户名:jack 密码:123456 登陆ing
login2(["password":"123456"]) //请输入用户名
login2(["username":"jack"]) //请输入密码
//隐式解包 (Implicitly Unwrapped Optional)
//某些情况下,可选项一旦被设定值之后,就会一直拥有值
//这种情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值
//可以在类型后门加个感叹号!定义一个隐式解包的可选项
let num1: Int! = 10
let num2: Int = num1
if num1 != nil {
print(num1 + 6)
}
if let num3 = num1 {
print(num3)
}
//尽量不要使用!,大多数情况下使用?的可选项
let num4:Int! = nil //加了!也是可选项,只不过是隐式解包的可选项
//let num5:Int = num4
//这里会报错:Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
//有一种情况,就是如果你希望别人传给你的参数必须是Int值,不能为nil,如果为nil,就崩溃掉,那就可以使用这种类型,这种也是不合理,还是期望使用?。
//字符串插值
//可选项在字符串插值或者直接打印时,编译器会发出警告
var age7: Int? = 10
print("my age is \(age7)") //my age is Optional(10)
//有三种方法可以消除警告
//1
print("my age is \(age7!)") //my age is 10
//2
print("my age is \(String(describing: age7))") //my age is Optional(10)
//3
print("my age is \(age7 ?? 0)") // my age is 10
//多重可选项
var num11: Int? = 10
var num22: Int?? = num11
var num33: Int?? = 10
print(num22 == num33) //true
var num111: Int? = nil
var num222: Int?? = num11
var num333: Int?? = nil
print(num222 == num333) //false
print(num111 == num333) //false
print((num222 ?? 1) ?? 2) //2
//这里(num222 ?? 1)强制解包之后是 num111,然后num111 ?? 2,因为num111 强制解包之后是nil,所以最终输入2
print((num333 ?? 1) ?? 2) //1
//这里 num333强值解包之后是nil,1 ?? 2,所以返回1
//可以使用lldb指令 frame variable -R 或者 fr v -R 查看区别
var num1: Int? = 10
var num2: Int?? = num1
var num3: Int?? = 10
var num1: Int? = nil
var num2: Int?? = num1
var num3: Int?? = nil
#这里有个注意点,如果num = 后面有值才是有意义的,如果是none是没有意义的