原文连接:http://blog.barat.cc/ios/understanding-swift-optional/

nil的遗憾

当某个变量或表达式没有任何内容时,在Objective-C中可以使用nil来表示。nil在Objective-C中是一个「野孩子」,void指针指向数字 0,本质上来讲nil就是一个数字。来看看下面的代码在Objective-C会出现什么情况:

int i = (int)(nil)+20; //可以这样吗?

nil可以用来做运算吗?编译器会报错?或者它可以运行?

因为nil指向数字 0,确切的说nil就是Int类型数据,所以上述的代码不仅可以通过编译,而且得到的结果是 20。显然,Objective-C编译器对nil的处理方式非常简单粗暴,直接将nil当作是数字对待了。这样做表面看起来平安无事,但在某些特定的情境下,却可能造成歧义。既然nil不能表示「纯粹无」,的确需要表示「这个变量没有任何值」该怎么办?看来,Objective-C对此是无解了。

Optional基础概念

或许是因为上述例子中提到的问题,在某些特定的场景下的确需要表示「无」的存在,因此Swift中引入了Optional类型。

在深入讨论之前,先看看Optional是什么吧!

public enum Optional<Wrapped> : _Reflectable,NilLiteralConvertible {
  case None

  case Some(Wrapped)

  public init()

  public init(_ some: Wrapped)

  @warn_unused_result
  public func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U? @warn_unused_result public func flatMap<U>(@noescape f: (Wrapped) throws -> U?) U? init(nilLiteral: ()) } 

可以看到Swift中Optional其实是一个枚举类型,其中包含了NoneSome两个值和应用某个规则并返回结果的mapflatMap两个方法,此外还有三个构造方法。我们可以使用上述的构造方法创造一个Optional类型的变量,但在实际开发过程中会更多的使用?表示一个变量是Optional类型。例如下面的示例:

var someNumber: Int?
var anotherNumber: Int = 100

var someStr: String?
var anotherStr: String = "Hello World"

使用问号?表示某个变量是Optional类型,如果没有显示赋值Swift会自动给Optional类型变量赋值nil。上述的代码中,声明了两个Int类型的变量,其中someNumber是Optional类型,而anotherNumber是普通的Int类型并且值为 100 。我们可以把Optional类型想象成为一个容器,装东西用的盒子。someNumber所代表的盒子里面是空的,等着用户往里面装东西「当然,能装的东西已经规定好了,必须是Int类型」,而anotherNumber所代表的盒子里面已经装好了东西。如下图:

变量存在的意义在于:可以参与运算并完成一定的业务要求。接下来我们对上述示例代码中的变量进行一定的运算,再观察结果分析。假设,给someNumberanotherNumber分别加上100并输出结果,那么代码如下:

var someNumber: Int?    //值为nil
var anotherNumber: Int = 100    //值为100

someNumber = someNumber + 100    //编译错误,不能对nil进行操作
anotherNumber = anotherNumber + 100    //正常

为什么会这样呢?按照在Objective-C中的理解看来:一个变量的值是nil,则指向数字0,是可以进行运算的「一开始的示例中我们正是这么做的」。显然,nil在Swift中已经不再是指向数字0的指针,而是真的指向「纯粹无」。既然当前这个变量的值是「纯粹无」,在它被初始化之前当然是不允许进行操作的。

变量someNumber盒子中没有任何值,所以不能进行运算,那么我们做如下的赋值再尝试:

var someNumber: Int?

someNumber = 100 //赋值
someNumber = someNumber + 100 //还是无法通过编译,不能对Optional直接操作

在Swift中对一个Optional类型的变量直接进行操作,是不允许的。这又是为什么呢?大家还记得吗?Optional是枚举类型,不经过任何转换直接和Int类型相加,当然是不允许的。那么,如果想要对Optional的值进行运算,要怎么办呢?

Optional类型转换

在对一个Optional类型的变量进行操作之前,需要先将其转换成可操作的具体类型。你可以把它理解成:在吃掉盒子里面存放的苹果之前,需要先将苹果从盒子中取出来。这个过程可以使用符号!来完成。

100
//赋值 someNumber = someNumber! + 100 //将苹果从盒子中取出来,再加上100

但是在将苹果从盒子中取出来的时候,你却需要面对一个严肃的哲学问题:盒子中确实有苹果吗?如果上述代码中缺少赋值表达式someNumber = 100,那么这段代码虽然可以躲过编译器的检查,但却会在程序运行过程中出现异常,导致应用崩溃。所以,为了保证程序的健壮性,在吃掉苹果之前,应该判断盒子中是否真的存在苹果,大致如下所示:

var someNumber: Int?

// someNumber = 100
if(someNumber != nil) {
    someNumber = someNumber! + 100
} else {
    print("盒子中根本木有苹果")
}

上述代码安全了,可是每个Optional类型的变量在使用之前,都需要对其进行if-else判断显然是一件很麻烦的事情,而人类是最喜欢偷懒的群体,那该怎么办呢?

其中第一个办法,称之为if-let绑定,通过if-let的判断对其进行操作,大致如下:

let authorName: String? = "Barat Semet"
let authorAge: Int? = 30

if let name: String = authorName,age: Int = authorAge {
        print("本文作者 \(name) 今年 \(age) 岁了")
} else {
    print("作者名称or年龄未指定")
}

另外一种方法称之为nil合并,使用两个??符号连接在一起表示:如果存在值则获取当前值,如果不存在则获取给定的默认值,大致代码如下:

var someNumber: Int? 

var number: Int = someNumber ?? 0 //若someNumber不为nil则获取其值,若为nil则获取0

上述打代码大致等价于:

var someNumber: Int?
var number: Int

if let unwrapped:Int = someNumber {
    number = unwrapped
} else {
    number = 0
}

swift-case和guard

我们已经看到了在处理Optional时如何使用if-elseif-let语法判断其值,此外Swift还提供了switch-caseguard语法,使我们的操作更加便捷。

使用switch-case结构对Optional的值进行匹配时,需要给每个case分之跟着的表达式加上?符号即可。如果对其中的某部分的值不关心,也可以使用SomeNone来简单处理,如下代码所示:

var changshaProgrammer: Double? = 10000000 //长沙市序猿总数

switch changshaProgrammer {
    case 0?: print("长沙市木有程序猿类")
    case (1..<1000)?: print("长沙市程序猿类竟然不到1000人")
    case .Some(let x): print("长沙市有 \(x)程序猿类")
    case .None: print("我们不知道长沙市有多少程序猿类")
}

除了上述这种方式之外,我们也可以使用guard语法做如下判断:

func sayProgrammerCount(count: Double) -> String {
    guard let pop: Double = count else {
        return "我们不知道长沙有多少程序猿类"
    }
    return "长沙有程序猿类 \(pop) 人"
}

print(sayProgrammerCount(1000000))

最后的总结

和Objective-C不同,在Swift代码中我们拿到某个表达式返回的Optional值时,如果非常确定该表达式的结果不会是nil则可以使用!将其强制转换为我们需要的结果。否则,在我们不确定的情况,一定要使用if-letswait-caseguard之中的一种对Optional返回的结果进行判断后再处理。

参考
  1. Understanding Optionals in Swift
  2. A Beginner’s Guide to Optionals in Swift
  3. The Swift Programming Language (Swift 2.1) The Basics
  4. The Swift Programming Language (Swift 2.1) Optional Chaining

理解Swift中Optional类型-有和无的哲学的更多相关文章

  1. ios – 异常断点处于活动状态时,应用程序在启动时崩溃

    我刚开始继续开发一款适用于商店的传统iPad应用程序.我注意到项目中的异常断点未启用.当我启用它时,应用程序在启动时崩溃,但在输出窗口中没有给出任何信息,而在线程视图中只有相当无用的信息(见下文)我试着解决它..>将Autolayout设置为关闭.>通过编辑和重新保存故事板文件..但到目前为止没有运气.我的猜测是,故事板中的某些内容被破坏了,因为AppDelegates“确实完成了启动……”

  2. ios – Swift 4添加手势:覆盖vs @objc

    我想在我的视图中添加一个手势,如下所示:但是,在Swift4中,我的编译器给出了以下错误:建议添加@objc以将此实例方法公开给Objective-C.实现此目的的另一个选项将覆盖touchesBegan()函数并使用它来处理点击.我试图以“Swift”的方式做到这一点,而不必带入Obj-C.有没有纯粹的Swift方式来添加这个轻击手势而不使用@objc?

  3. ios – Objective-C中“and”关键字的含义是什么?

    我在Xcode中输入了一条评论,但忘了领先//.我注意到了这一点并且突出显示为关键字.我做了一些谷歌搜索,但我似乎无法弄清楚它做了什么.这是什么意思?解决方法它是&&的同义词.见iso646.h.

  4. ios – 以编程方式在Swift中添加联系人

    我想在Swift中以编程方式添加联系人.我发现了一些Objective-C示例,但我没有让它们工作,甚至在Objective-C中也没有.我不希望这涉及到AddressBookUI,因为我想从我自己的UI中获取值.解决方法这是在Swift中添加联系人的快速方法.我在我的iPhone5iOS7.1上验证了它,因为我发现模拟器并不总是与我的手机对AB的东西相同.您可以添加一个按钮并指向此方法:顺便说一下–它假设你已经分配了一个地址簿var,你可以通过覆盖viewDidAppear来打开视图.它也会执行安全提示

  5. ios – 在AFNetworking中设置用户代理

    我们的iOS应用程序最近被Apple拒绝,因为它无法与我们的服务器api建立有效连接.我们使用特殊格式的用户代理来注册设备令牌等.如果用户代理不适合我们的sheme,则api会阻止请求.这一切都很好地在模拟器和真实设备上测试应用程序.用户代理设置正确,api调用工作正常.当Apple测试应用程序时,他们拒绝了它,因为应用程序无法连接到api.当我们检查服务器日志文件时,我们注意到,Apple测试人

  6. ios – 如何从变量访问属性或方法?

    是否可以使用变量作为Swift中方法或属性的名称来访问方法或属性?在PHP中,您可以使用$object->{$variable}.例如编辑:这是我正在使用的实际代码:解决方法你可以做到,但不能使用“纯粹的”Swift.Swift的重点是防止这种危险的动态属性访问.你必须使用Cocoa的Key-ValueCoding功能:非常方便,它完全穿过你要穿过的字符串到属性名称的桥,但要注意:这里是龙.

  7. ios – 将iphone应用程序重定向到苹果商店

    我有一个iPhone应用程序,当有可用的升级时,我想提示用户升级,如果他们点击升级,我想将它们重定向到苹果商店.这是怎么做到的?

  8. ios – 如何从Apple Watch调用iPhone上定义的方法

    有没有办法从Watchkit扩展中调用iPhone上的类中定义的方法?根据我的理解,目前在Watchkit和iPhone之间进行本地通信的方法之一是使用NSUserDefaults,但还有其他方法吗?

  9. ios – Objective-C中的Google用户serverAuthCode nil

    我正在尝试将GoogleSignIn框架集成到iOS应用程序中,并对服务器上的用户进行身份验证.我设法登录用户,但在–(void)signIn:(GIDSignIn*)signIndidSignInForUser:(GIDGoogleUser*)用户withError:(NSError*)错误委托方法,user.serverAuthCode为nil,我需要通过此服务器身份验证代码,嗯,验证服务器上

  10. ios – Ad-hoc应用程序始终无法安装

    所以我在使用ios5的ipad1上安装ad-hoc分发应用程序时遇到问题.我在其他ipads中安装时没有这个问题…

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部