购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

Tip 18

初始化返回nil

在Objective-C中,init方法除了返回self以外,其实和一个普通的实例方法并没有太大区别。如果你喜欢的话,甚至可以多次进行调用,这都没有限制。一般来说,我们还会在初始化失败(比如输入不满足要求无法正确初始化)的时候返回nil来通知调用者这次初始化没有正确完成。

但是,在Swift中默认情况下初始化方法是不能写return语句来返回值的,也就是说我们没有机会初始化一个Optional的值。一个很典型的例子就是初始化一个url。在Objective-C中,如果我们使用一个错误的字符串来初始化一个NSURL对象,返回会是nil,代表初始化失败。所以下面这种“防止度娘吞链接”式的字符串(注意两个t之间的空格和中文式句号),也是可以正常编译和运行的,只是结果是个nil:

但是在Swift中情况就不那么乐观了,-initWithString:在Swift中对应的是一个convenience init方法:init(string URLString:String!)。上面的Objective-C代码在Swift中等效为:

init方法在Swift1.1中发生了很大的变化,为了将来龙去脉讲述清楚,我们先来看看它在Swift1.0中的表现。

Swift 1.0及之前

如果在Swift1.0的环境下尝试运行上面代码的话,我们会得到一个EXC_BAD_INSTRUCTION,这说明触发了Swift内部的断言,这个初始化方法不接受这样的输入。一个常见的解决方法是使用工厂模式,也就是写一个类方法来生成和返回实例,或者在失败的时候返回nil。Swift的NSURL就做了这样的处理:

在使用的时候:

不过虽然可以用这种方式来和原来一样返回nil,但是这也算是一种折中。在可能的情况下,我们还是应该尽量降低出现Optional的可能性,这样更有助于代码的简化。

如果你确实想使用初始化方法而不愿意用工厂函数,也可以考虑用一个Optional量来存储结果

这样你就可以处理初始化失败了,不过相应的代价是代码复杂度的增加

Swift 1.1及之后

虽然在默认情况下不能在init中返回nil,但是通过上面的例子我们可以看到Apple自家的API还是有这个能力的。

好消息是在Swift1.1中Apple已经为我们加上了初始化方法中返回nil的能力。我们可以在init声明时在其后加上一个?或者!来表示初始化失败时可能返回nil。比如为Int添加一个接收String作为参数的初始化方法。我们希望在方法中对中文和英文的数据进行解析,并输出Int结果。对其解析并初始化的时候,就可能遇到初始化失败的情况:

所有的结果都将是Int?类型,通过Optional Binding,我们就能知道初始化是否成功,并安全地使用它们了。我们在这类初始化方法中还可以对self进行赋值,也算是init方法里的特权之一。

同时像上面例子中的NSURL.URLWithString这样的工厂方法,在Swift1.1中已经不再需要。为了简化API和提高安全性,Apple已经被标记为不可用了并无法编译。而对应地,可能返回nil的init方法都加上了?标记:

在新版本的Swift中,对于可能初始化失败的情况,我们应该始终使用可返回nil的初始化方法,而不是类型工厂方法。 nkSBbWRXZ2eqoQB525ZzENy5kOB8/eeZbnaYqg4HPULerp9pyzATzaGjueyV2dZ9

点击中间区域
呼出菜单
上一章
目录
下一章
×