在OC中,我们通常会遇到这种写法
NSString *urlString = @"";
NSURL *url = [NSURL URLWithString:urlString];
// NSError
NSError *error;
// 这里的 String初始化方法可能会创建失败
NSString *result = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
只有result
等于 nil的时候 才应该去判断error
是否为空
if (result != nil){
// do something ...
}
//只有result 等于 nil的时候 才应该去判断error是否为空
// 检查 error ,非nil 的error 并不是一个有效的指针,直接使用可能造成异常
if (error) {
// error do something...
}
实际上OC中 向nil发送消息是安全的,但是这种方式显得很繁琐
Swift中的可选值
Swift中的枚举 可以在它们的成员中包含另外的关联的值,Optional 也是通过枚举实现的
enum Optional {
case none
case some(Wrapped)
}
可选值的使用
完整的声明如下:
var num1:Optional //Optional
num1 = .some(10) // Optional(10)
num1 = .none // nil
由于可选类型在Swift中很常见,有更简洁的声明如下,推荐!!!
OptionalOptional
遵守ExpressibleByNilLiteral
协议可以用 nil 替代 .none
var num2:Int? // Int?
num2 = 10 //Optional(10)
num2 = nil //nil
num2? = 10 // nil
注意,这里的num2? = 10,当num2的真实值不为nil的时候,num2? = 10才会赋值成功,否则就是nil,这是可选链调用
包装一个非可选值,非可选值转换为可选
var ch:Character = "4" // Character
var optionalCh = Optional(ch) //Character?
获取枚举关联值的唯一方法是通过模式匹配,枚举的模式匹配
switch num1{
case .none: break //none 什么都不做
case .some(let value):print(value)
}
这种通过模式匹配看着很有些复杂,Optional这个枚举还能简化一下:
switch num1{
case nil:break
case let v?:print(v)
}
可选值绑定
用枚举的模式匹配处理可选类型是有些复杂,可选类型作为Swift的一大亮点,系统当然提供了更加便利的方案:
if - let
if - let 语句会检查可选值是否为 nil,如果不是 nil,便会解包可选值。并且它可以是多个表达式的组合,可选值的解包结果可以作为后续表达式的值,解包后的作用域介于{}之中
if let num = num1 , num > 0{
// do num operator ...
}
当然if - let 语句也支持多个可选值的解包
if let lnum = num1 , rnum = num2{
// do lnum and rnum operator ...
}
if 后面可以是 var , if - var 。var对应的解包值可以在作用域内修改,但这是对原解包结果的拷贝,实际并不会修改原始值
let num1:Int? = 10
if var num = num1 {
num += 1
print(num) // 11
}
print(num1) // Optional(10)
while - let
while let 语句和 if let 非常相似,同样 let可以是var,可以是多个表达式的组合,唯一不同的是它表示当表达式的结果返回 nil 时便终止循环
//readLine()从“readLine 函数从标准输入中读取内容,并返回一个可选字符串。当到达输入末尾
//时,这个函数将返回 nil”
while let n = readLine(){
print(n)
}
guard - let
guard let 更像是 if not let 的反义,可选解包不为nil的作用域从当前guard语句开始一直到离开作用域前。在 guard 的 else 代码块中,你可以执行任意代码,但是必须提供一个离开当前作用域的方式,例如:return,异常抛出fatalError()
func doSomething(_ value:Int?){
guard let v = value else{
return
}
//v的作用域一直到函数结束
print(v)
//...
}
可选链
在OC中对nil对象发送消息是安全的,Swift中可选链也可以做到
delegate?.handle() //delegate 如果为nil handle将不会被执行
OC中的Block在调用之前需要判断是否为null,否则也会crash,Swift中的可选链在调用闭包也是没问题的
let block:((String) -> Int)? = nil
block?("123")
调用可选链得到的返回值会自动包装成可选类型
let result = block?("123") //Int?
多个可选链的结果,编译器会帮我们展开,
extension Int{
var opt:Self?{
Optional(self)
}
}
//以下调用结果不是 Int??? 而是Int?
var op = 5.opt?.opt?.opt // Optional(5)
可选链链赋值,可选变量赋值,如果加上?会判断当前变量是否为nil,如果为nil就会赋值失败
var num2:Int? = nil // 不初始化 默认是也nil
num2? = 10 // nil
num2 = 20 // 无条件赋值
num2? = 30 // Optional(30)
nil合并运算符
刚开始在使用可选值的时候,编译器会有这样的提示:Provide a default value to avoid this warning
这句话意思是需要提供一个如下形式的默认值:nil合并运算符 ??
num1 ?? default value // 需要提供一个默认值
注意??两侧的类型必须一致,左侧必须是可选类型。比如左侧是Optional(Int),右侧必须是Int类型
看一个例子:这行代码,如果不加 ??是不能编译通过,因为字符串转int有可能失败,Int.init()返回值是个可选值,如果Int("123")初始化成功,返回解包后的值,失败返回??后面的值。
var number:Int = Int("123") ?? 0
其中的??的行为类似于三目运算符:
var intValue = Int("123") //Optional(Int)
number = (intValue!) != nil ? intValue! : 0
多个可选值通过??链接,表达式的结果是选择第一个不为nil的结果
var a:Int? = nil
var b:Int? = 2
var c:Int? = 3
var result = a ?? b ?? c ?? 0 //2
强制解包
上文提到的解包方式都是可选解包,只有当结果不为nil的时候,才会进行下一步。而强制解包是通过在可选值后面加上!,这种方式看似简洁,但是会忽略编译器警告,而且当结果为nil时会直接闪退
var number = Int("123")! // 123
var number1 = Int("123a")! // Fatal error: Unexpectedly found nil while unwrapping an Optional value
强制解包时机
当你能确定你的某个值不可能是 nil 时可以使用叹号,你应当会希望如果它意外是 nil 的话,程序应当直接挂掉
隐式解包
隐式解包,是在声明类型的时候,加上!。注意区别强制解包
这种方式声明的变量,在使用的时候系统会强制解包,因此看上去不像是一个可选类型。但是一旦值为nil,使用时候依然会异常(使用的时候系统会强制解包)。
let number3:Int! = Int("123") // Int!仍然是可选类型
number3 + 1 // 124 系统强制解包number3,如果number3为nil,闪退。否则正常运行
隐式解包,仍然是可选类型,可以对它进行可选解包
if let number4 = number3{
number4 // int
}