详解Swift中Optional

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
查看查看259 回复回复6 收藏收藏 分享淘帖 转播转播 分享分享 微信
查看: 259|回复: 6
收起左侧

详解Swift中Optional

[复制链接]
firefighter 发表于 2016-9-5 12:35:05 | 显示全部楼层 |阅读模式
快来登录
获取优质的苹果资讯内容
收藏热门的iOS等技术干货
拷贝下载Swift Demo源代码
订阅梳理好了的知识点专辑

详解Swift中Optional

详解Swift中Optional - 敏捷大拇指 - 详解Swift中Optional





1、Optional

Swift中的Optional作为一种类型,既可以存储一个值,也可以为空(也就是swift里的nil),通常在类型后面加一个?表示它是Optional类型的:

[Swift] 纯文本查看 复制代码
var number: Int? = 32


其实?只不过是一个语法糖,Optional的实际类型是一个enum:

[Swift] 纯文本查看 复制代码
enum Optional<T>: _Reflectable, NilLiteralConvertible {
    case None
    case Some(T)
    //...
}


上面的var number: Int? = 32 也就可以表示为:

[Swift] 纯文本查看 复制代码
var numbet: Optional<Int> = 32


当然我们习惯上更习惯于表示为 Int?




2、Optional的作用

一个Optional对象只存在两种状态:包含一个值,或者为空,我们都可以通过解包(unwrap)来获取。下面一段出自The Swift Programming Language (Swift 3)

The concept of optionals doesn’t exist in C or Objective-C. The nearest thing in Objective-C is the ability to return nil from a method that would otherwise return an object, with nil meaning “the absence of a valid object.” However, this only works for objects—it doesn’t work for structures, basic C types, or enumeration values. For these types, Objective-C methods typically return a special value (such as NSNotFound) to indicate the absence of a value. This approach assumes that the method’s caller knows there is a special value to test against and remembers to check for it. Swift’s optionals let you indicate the absence of a value for any type at all, without the need for special constants.


Swift通过引入Optional解决了Objective-C中“有”与“无”的问题,使代码的安全性得到了很大的提高,同时我们也应该知道,Swift是一种类型安全的语言。

在某些场景下,Optional能起到很大的作用:

  • 当一些属性值可以为空时,比如一个Person类中,middleName、spouse这类的属性都可以为空
  • 当一个方法可以返回空值,比如类型转换函数,官方文档的例子

[Swift] 纯文本查看 复制代码
//try to convert a String into an Int
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?", or "optional Int"
// 如果possibleNumber 是“hello”,则转换不会成功,就会返回nil

  • 如果在一个字典中,使用key获取对应的value时,返回的也应该是Optional的值,因为你也可能找不到key对应的value,此时返回nil
  • 一个方法可以返回一个值,如果方法内部产生了错误,也可以什么都不返回
  • Delegate 属性(不总是需要被赋值)
  • class中weak 类型的属性,他们所指向的值可以为空
  • 一个大的资源可以随时被释放,以节约空间,所以正常情况下都是空的,只有使用时才会请求赋值。





3、Optional Binding

出于类型安全的考虑,我们不能再把Optional当作Boolean值处理。像下面这条语句在Swift中会遇到编译错误:

[Swift] 纯文本查看 复制代码
var myString: String? = "Hello"
if myString {
    print(myString)
}


但是你可以通过==和!=,将Optional值和nil做比较来判断它是否包含一个值。如果不包含任何值,则为空。

[Swift] 纯文本查看 复制代码
if myString != nil{
    print("myString contain a string value of \(myString!)")
}


在上面的语句里,当我们确定myString包含一个值时,我们通过在myString后面添加一个!来进行强制解包(forced unwrapping),获取Optional内包含的值。

但是实际上,Swift提供了一种更加方便的形式来完成这一过程,所谓的Optional Binding,看下面的代码:

[Swift] 纯文本查看 复制代码
if let actualString = myString {
    print("myString contain a string value of \(actualString )")
} else {
    print("myString is nil")
}


上面代码的意思是,如果optional string 包含一个值,我们就把这个值赋给actualString,然后就可以在if语句里继续使用它了,所以不再需要对其进行解包了,因为actualStr这样我们就用Optinal binding 代替了强制解包(forced unwrapping)。我们也可以使用if var actualString = myString 来获取actualString,则这个actualString就是var类型的。




4、隐式解包Optional

相较于普通的Optional值,在Swift中我们还有一种特殊的Optional,在对它的成员或者方法进行访问时,编译器会自动进行解包,被称为隐式解包Optional(ImplicitlyUnwrappedOptional),在声明时,通过在类型后面添加!来告诉编译器这是一个隐式解包Optional:

[Swift] 纯文本查看 复制代码
let possisbleString: String!


隐式解包的Optional本质上与普通的Optional值并没有什么不同,只是在访问时,编译器会自动帮我们完成在变量后插入 ! 的行为:

[Swift] 纯文本查看 复制代码
let possibleString: String! = "An implicity unwrapped optional string."
let implicitString: String = possibleString //此处我们不需要!来对possibleString 进行显示解包


很显然,隐式解包的写法会带来一个潜在的危险,如果尝试访问一个为空的隐式解包Optional, 就会遇到一个runtime error。那么Swift为什么要引入隐式解包Optional呢,王巍在他的Swift Tip解释了一下:

这一切都是历史的锅。因为Object-C中Cocoa的所有类型变量都是可以指向nil的,有一部分Cocoa的API中在参数或者返回时即使被声明为具体的类型,但是还是可能在某些特定的情况下是nil, 而同时也有另一部分API永远不会接受或者返回nil。在Objective-C时,这两种情况并没有加以区别,因为在OC中向nil发送消息是允许的,结果就是什么都不会发生,而在Cocoa API从OC转为Swift的module声明的自动化工具里,是无法判定是否存在nil的可能的,因此也无法决定哪些类型应该是实际的类型,而哪些类型应该声明为Optional。

在这种自动化转换中,最简单粗暴的应对方式是全部转为 Optional,然后让使用者通过 Optional Binding 来判断并使用。虽然这是最安全的方式,但对使用者来说是一件非常麻烦的事情,我猜不会有人喜欢每次用个 API 就在 Optional 和普通类型之间转来转去。这时候,隐式解包的 Optional 就作为一个妥协方案出现了。使用隐式解包 Optional 的最大好处是对于那些我们能确认的 API 来说,我们可直接进行属性访问和方法调用,会很方便。但是需要牢记在心的是,隐式解包不意味着 “这个变量不会是 nil,你可以放心使用” 这种暗示,只能说 Swift 通过这个特性给了我们一种简便但是危险的使用方式罢了。


我们可以在类初始化时使用隐式解包Optional,官方文档里这么描述的:Unowned References and Implicitly Unwrapped Optional Properties

我们也可以像使用一个一般的Optional一样使用隐式解包Optional。

比如判断是否为空:

[Swift] 纯文本查看 复制代码
if possibleString !=nil {
    //some action
}


比如使用if let 来进行optional binding:

[Swift] 纯文本查看 复制代码
if let implicitString = possibleString {
    print(implicitString)
}


当你知道变量可能为空的时候,不要使用隐式解包Optional。如果一个变量在它的声明周期里可能包含控制,那你总是需要使用一般的Optional。




5、Optional Chaining

Optional Chaining,如同名字一样,我们可以通过一个链来安全的访问一个Optional的属性或者方法。

可以参看一个例子:

[Swift] 纯文本查看 复制代码