先来说说Optional
的概念,以方便之后的理解。来看一下下面的代码:
var a : String = nil // 编译错误,String类型不能为nil
var b : String = “Hello!”
Swift
中的普通类型不再能设为nil
。那如何表示这个值不存在呢?所以引进了Optional
的概念:代表 nil
或某个具体的值
。例如:
var c : String? = nil
var d : String? = “Hello!”
String?
就是一个Optional
,它既能够被具体类型
赋值, 也可以赋值为nil
。通过 String
和 Optional
的比较,发现Optional
就相当于把具体类型和 nil
打包捆绑在了一起,转变成了一种新的类型。
Optional 有两种声明方法:
var apple: String?
var bread: String!
很多人认为,声明为 String!
的变量表明该变量的值不为 nil
。但是,实际上,String!
和 String?
都是有默认值的,且默认值都为nil
。我们为它们赋一个初值再打印类型来看看:
var apple: String? = “apple”
var bread: String! = “bread”
(lldb) p apple
(String?) $R0 = “apple”
(lldb) p bread
(String?) $R1 = “bread”
发现,尽管apple
和 bread
虽然一个是 String?
一个是 String!
但是打印出来都是 String?
类型的。
所以更恰当的理解应该是:String! 只是理解意义上的不为nil,其本质还是一个 Optional,从声明来说它和 String? 完全等价,所以也能够赋值为 nil 。
为了之后叙述方便,我们把定义类似 String?
的称为 Optional(?)
, 定义类似 String!
的称为 Optional(!)
。
Optional
的本质是囊括 nil
和具体类型的一种枚举。获取它具体值的操作过程称之为拆包,用 “!”
表示。先来做个实验:
var string : String
var optionalString: String?
var nonOptionalString: String!
string = nonOptionalString // ① 崩溃
string = optionalString // ② 编译错啦
string = optionalString! // ③ 崩溃
具体看一下报错原因:
①
String!
赋值给String
,报错为:拆包时Optional
的值为nil
;②
String?
赋值给String
,编译器报错为Optional
未拆包;③ 拆包后的
String?
赋值给String
,这时,报错为:拆包时Optional
的值为nil
;
String!
和 拆包后的 String?
进行传值时报的错相同,由此我们可以得出一个结论:
编译器默认 Optional(!)
是确定有值的,所以不需要再对 nil
的情况进行处理。Optional(!)
的变量只是给予了自动拆包的权限,在实际使用它的过程中不需要再次使用 ‘!’
进行拆包。在swift
官方文档中称为:Implicitly Unwrapped Optional
(隐式拆包),也可以理解成“拆过包了”。
但是,只有在变量确认有值的情况下才能进行拆包。就像如上代码,optionalString
为nil
,进行拆包就会引发崩溃。swift
官方建议,Optional
使用 if +
! 结合的方式或者 if let
进行安全地拆包。简单的看下代码:
if optionalString != nil {
append(string: optionalString!)
} else {
// do something
}
if let actualString = optionalString {
append(string: actualString)
} else {
// do something
}
再次提醒一下,Optional(!)
和 Optional(?)
都可以使用这两种方式进行安全地拆包,只是编译器不会对没有处理 nil
情况的 Optional(!)
报错。
下图展示了 Optional
和 String
可接收类型的比较:
String? | String! | String |
---|---|---|
String? | String? | String! |
String! | String! | String |
String | String | |
nil | nil |
拆完包之后的 Optional
其实就是 String
类型。编译器强制使用者在变量为 nil
的时候要进行处理,否则就会报错会崩溃。String!
是为了规避变量一定不为 nil
的情况下却要反复判断是否为 nil
的冗余代码而产生的。例如,我们在使用 IBOutlet
时,一定会定义成 Optiona(!)
。
String!
在声明时和 String?
完全等价,在使用时和 String
完全等价。