Swift学习笔记 | Optional

Optional###

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

var number: Int? = 32

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

enum Optional<T>: _Reflectable, NilLiteralConvertible {
    case None
    case Some(T)
    //...
}

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

var numbet: Optional<Int> = 32

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

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这类的属性都可以为空
  • 当一个方法可以返回空值,比如类型转换函数,官方文档的例子
//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 类型的属性,他们所指向的值可以为空
  • 一个大的资源可以随时被释放,以节约空间,所以正常情况下都是空的,只有使用时才会请求赋值。

Optional Binding###

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

var myString: String? = "Hello"
if myString {
    print(myString)
}

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

if myString != nil{
    print("myString contain a string value of \(myString!)")
}

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

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

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类型的。

隐式解包Optional###

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

let possisbleString: String! 

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

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
比如判断是否为空:

if possibleString !=nil {
    //some action
}

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

if let implicitString = possibleString {
    print(implicitString)
}

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

Optional Chaining###

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

if let isPNG = imagePaths["star"]?.hasSuffix(".png") {
    print("The star image is in PNG format")
}

这里通过在imagePath["star"]后面添加?来获取star所对应的图片路径,如果不存在就直接是nil,如果存在则返回对应的path,然后紧接着调用path的hasSuffix方法。

使用Optional Chaining可以让我们摆脱很多不必要的判断和取值,从而精简代码。

??的使用###

当Optional解包后的值为nil时,我们可以通过使用??来设置一个默认值。

let defaultImagePath = "/images/default.png"
let heartPath = imagePaths["heart"] ?? defaultImagePath
print(heartPath)

如果imagePaths中没有包含"heart"对应的value,则会把defaultImagePath赋给heartPath,也就会打印出/images/default.png

当然了,我们也可以将??链接起来,设置多重默认值:

let shapePath = imagePaths["cir"] ?? imagePaths["squ"] ?? defaultImagePath

这样每一次解包值为nil时都会设置默认的值。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,706评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,002评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,462评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,375评论 0 216
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,763评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,849评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,033评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,768评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,490评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,734评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,204评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,566评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,227评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,137评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,934评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,926评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,774评论 2 274

推荐阅读更多精彩内容

  • 基础部分(The Basics) 当推断浮点数的类型时,Swift 总是会选择Double而不是Float。 结合...
    gamper阅读 1,196评论 0 7
  • 2014年的苹果全球开发者大会(WWDC),当Craig Federighi向全世界宣布“We have new ...
    yeshenlong520阅读 2,191评论 0 9
  • 一直没有时间好好看一下swift,最近复习了一遍语法,这里记录swift学习过程中遇到的一些问题和要点,和Obje...
    bomo阅读 2,272评论 0 25
  • 对各种值为"空"的情况处理不当,几乎是所有Bug的来源。 在我们的例子里,尽管tmp的值是nil,但调用tmp的r...
    AKyS佐毅阅读 10,402评论 1 13
  • 01 思考与讨论 “选择”与“行动” 1.你最近面临的一个选择困境是什么,你有哪些解决它的方法选项?2.你有被“拖...
    朵朵西里阅读 336评论 0 1