Swift - 关于 Optional 的一点唠叨 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
swiftcafex
V2EX    Swift

Swift - 关于 Optional 的一点唠叨

  •  
  •   swiftcafex 2015-12-27 13:55:18 +08:00 3498 次点击
    这是一个创建于 3638 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Optional 是 Swift 的一个非常重要的特性,它除了提供类型安全的机制,也是 Swift 中很多语言特性的核心。当然,使用 Optional 时也要了解很多坑,这样能帮助我们更好的运用它。

    Optional

    Optional 是 Swift 中一种特殊的类型,它本身有一个枚举的定义,简单来说就是这个形式:

    enum Optional { case None case Some } 

    当然, Swift 中这个枚举的实际定义要复杂的多,这里只为了帮助大家最简单的了解。一个 Optional 的值,要么是空(None), 要么就会包含一个值(Some)。

    比如我们可以声明一个 Optional 的 String 类型的变量,只需要在变量定义的时候在类型后面加上一个 ?

    var name: String? 

    如果这个变量是标识成 Optional 的,我们在引用它的时候就必须做一些特殊的处理,可以使用强制解包:

    print(name!) 

    在变量后面添加一个 ! ,相当于告诉编译器,我确信这个变量不是 nil ,可以直接使用(当然,使用强制解包只代表你自己确认它不为 nil ,但它还是有可能为 nil 的,如果这样的情况发生,依然会造成程序运行时崩溃)。

    相比使用强制解包,更加安全和优雅的方式是使用 Optional Chaining :

    if let nameValue = name { print(nameValue) } 

    使用 if let 这样的语法就可以更加安全的操作 Optional 值。只有在 name 中的值不为 nil 的时候, nameValue 变量才会被初始化成功。 这样我们的 print 语句就不会因为 nil 而崩溃。

    虽然我们使用 Objective-C 的时候也可以进行类似这样的判断 if value != nil { ... }, 但 Optional 的好处是,它是编译级别的,只要一个值被标识成 Optional 的,它就必须在引用的时候进项非空判断,无论你使用强制解包还是 Optional Chaining 。这样我们代码的类型安全就得到很大的增强。

    Optional Chaining 陷阱

    相信上面对于 Optional 以及 Optional Chaining 的介绍,大家或多或多少已经了解过了。下面咱们就来说说一些 Optional Chaining 的小细节。

    既然叫做 Optional Chaining ,顾名思义,它是可以进行链式操作的。也就是说,我们可以连续调用 Optional 相关操作,比如,我们有这样的类结构:

    struct Name { var firstName: String = "" var lastName: String = "" } struct Person { var name: Name? var age: Int } 

    然后,我们这样进行调用:

    var person:Person? = Person(name: Name(firstName: "san", lastName: "Zhang"), age: 18) print(person?.name?.firstName) // 输出? 

    这时候 print 语句的输入是什么呢? 如果看 firstName 属性的定义的话:

    var firstName: String 

    是不是会认为会直接输出 san 呢? 但并不是这样,输出的结果会变成这样:

    "Optional("san")" 

    firstName 明明不是 Optional 类型的值,怎么会输出成 Optional 的呢,这时因为 firstName 虽然本身不是 Optional 的,但它却处在 Optional Chaining 中,我们看一下它的整个引用:

    person?.name?.firstName 

    这个引用中, person 和 name 都是 Optional 的。只要一个表达式中有一个 Optional 的值,整个表达式的结果就都是 Optional 的,不论最后一个属性本身是否是 Optional 的。

    仔细想想这样是很合理的,比如我们这个表达式中,如果 person 是 nil 呢?那么这个 Optional Chaining 就会提前返回,因为 person 都是 nil 了,后面的属性引用就没有意义了。所以我们就需要对这个表达式进行 Optional 处理。

    那么, 正确的引用方式应该是这样:

    if let firstName = person?.name?.firstName { print(firstName) } 

    现在, print 语句的输出就正常了。同样的, Optional Chaning 作为函数返回值也需要注意:

    func getName(person: Person) -> String { return person.name?.firstName } 

    同样的道理, getName 函数返回的是一个 String 类型。 firstName 属性也是 String 类型。但这个函数定义编译不会通过。和我们刚才的将的是同样的道理,因为 return 语句的表达式也是一个 Optional Chaining 。所以我们的函数需要定义成这样:

    func getName(person: Person) -> String? { return person.name?.firstName } 

    实际应用

    说了这些 Optional 的特性,也举了一些简单的例子。这些特性在我们的日常开发实践中也很常见,比如:

    class WebViewDelegate :NSObject, UIWebViewDelegate { func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool { if let absURL = request.URL?.absoluteString { // do something.. return false } return true } } 

    我这里的 UIWebView 的代理对象,会在每次加载网页的时候对页面的地址进行处理。 首先要取到页面的地址:

    if let absURL = request.URL?.absoluteString { //... } 

    这里的 request.URL?.absoluteString 就是一个 Optional Chaining ,所以我们要先将它解包出来,然后再进行处理。

    如果我们没注意这个的话,很天真的使用这种形式:

    if request.URL?.absoluteString == "xxx" { } 

    就会产生编译错误了,还会耗费很多时间去调试~

    结语

    Optional 是 Swift 最核心的特性之一,使用得当,它能够提高我们开发的效率,以及程序的安全性,好处多多。当然也要深入去了解它的特性,这样我们就能避免它产生的陷阱,从而更加游刃有余的徜徉在 Swift 的海洋中。

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     838 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 21:18 PVG 05:18 LAX 13:18 JFK 16:18
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86