让我们来看一个例子:
class Father{
var child: Child = Child()
...
}
class Child{
var pet: Pet = Pet()
}
class Pet{
func jump(){
print("pet jump")
}
}
如上,父亲有小孩,小孩有狗,狗会叫。
但是有可能小孩没有宠物呢?怎么办?
这不是很简单吗,我们来改写下小孩类不就好了:
class Child{
var pet: Pet = nil
}
Oh no,编译器报错了。啥?nil不能初始化pet?
为啥会出错呢,原因是Swift中,所有的变量都需要初始化,不能为nil。
那咋办?Swift用Optional来解决这个问题。
Optional
我们改写一下小孩类:
class Child{
var pet: Pet?
}
Pet?等价于Optional<Pet>
就是说你也可以这么写:
class Child{
var pet: Optional<Pet>
}
这时,小孩就没有小狗了,变量pet变成了一个Optional类型。
我们可以把Optional想像成一个盒子,盒子里面装了变量的值,当然盒子也有可能是空的。
那么上面的例子现在就变成:父亲有小孩,小孩有装狗用的盒子,盒子里面没有小狗。
要把小狗装进盒子很简单:
var child: Child = Child() child.pet = Pet()
Optional的 拆包(unwrapping)
强拆(Force unwrapping)
好了,现在我们把宠物取出来让他跳一下吧!
var pet = child.pet
pet.jump()
啊?啥又报错了。注意了,这时的pet变量的类型不是Pet,而是Optional,Optional没有跳的方法,他是个盒子,我们要把真正的宠物取出来才能让它跳。
这样取:
var pet = child.pet!
pet.jump() //输出:pet jump
像上面这样,加个感叹号就可以把把东西从Optional里取出来了。这种方式叫做强拆,不管三七二十一,里面的东西给我拿出来。
我们应该尽量少直接用强拆,因为我们不知道盒子里面有没有东西。像下面这样就会报错:
var child: Child = Child()
var pet = child.pet!
因为我们没有给child的pet盒子装宠物,所以我们要加个判断,看看盒子里面是不是空的:
var child: Child = Child()
if (child.pet != nil){
var pet = child.pet!
pet.jump()
}else{
print("Box is empty");
}
//输出:Box is empty
每次都这样判断很麻烦吖,而且万一忘记了就可能会报错,更重要的是这个报错是运行时报的,编辑器检查不出来。(这就是常见的空指针异常)
if let
Swift提供了一种更好的方法:
var child = Child()
if let pet = child.pet{
pet.jump()
}else{
print("Box is empty");
}
//输出:Box is empty
这样,如果有宠物直接取出来放到pet常量里面,没有的话就执行else。(连拆包的感叹号也不用写了呢)
问号拆
很多情况下我们想child有pet我们就让它跳,没有就不做处理。
写起来是这样的:
var child = Child()
if let pet = child.pet{
pet.jump()
}
Swift给我们提供了个更方便的写法:
var child = Child()
child.pet?.jump()
上面这段代码什么都不会发生,如果pet为空,那么问号之后的代码都不会执行。
如果Father中的Child也是个Optional,我们还可以这样:
var father = Father()
father.child?.pet?.jump()
解释起来就是,father有child吗?child有pet吗?pet jump