Swift: 如何理解Swift中Optional的!和?

先来说说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。通过 StringOptional的比较,发现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”

发现,尽管applebread 虽然一个是 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 (隐式拆包),也可以理解成“拆过包了”。

但是,只有在变量确认有值的情况下才能进行拆包。就像如上代码,optionalStringnil,进行拆包就会引发崩溃。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(!) 报错。

下图展示了 OptionalString 可接收类型的比较:

String? String! String
String? String? String!
String! String! String
String String
nil nil

拆完包之后的 Optional其实就是 String 类型。编译器强制使用者在变量为 nil 的时候要进行处理,否则就会报错会崩溃。String! 是为了规避变量一定不为 nil 的情况下却要反复判断是否为 nil 的冗余代码而产生的。例如,我们在使用 IBOutlet 时,一定会定义成 Optiona(!)
String! 在声明时和 String? 完全等价,在使用时和 String 完全等价。

你可能感兴趣的:(Swift: 如何理解Swift中Optional的!和?)