Swift —— Optional

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

nil的遗憾

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

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

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

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

Optional基础概念

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

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

publicenumOptional :_Reflectable,NilLiteralConvertible{

caseNone

caseSome(Wrapped)  

publicinit()  

publicinit(_some:Wrapped)  

@warn_unused_result  

publicfuncmap(@noescape f:(Wrapped)throws -> U)rethrows->U?

@warn_unused_resultpublicfuncflatMap(@noescape f:(Wrapped)throws -> U?)rethrows->U?

publicinit(nilLiteral:())}

可以看到Swift中Optional其实是一个枚举类型,其中包含了None、Some两个值和应用某个规则并返回结果的map、flatMap两个方法,此外还有三个构造方法。我们可以使用上述的构造方法创造一个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所代表的盒子里面已经装好了东西。如下图:

变量存在的意义在于:可以参与运算并完成一定的业务要求。接下来我们对上述示例代码中的变量进行一定的运算,再观察结果分析。假设,给someNumber、anotherNumber分别加上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类型的变量进行操作之前,需要先将其转换成可操作的具体类型。你可以把它理解成:在吃掉盒子里面存放的苹果之前,需要先将苹果从盒子中取出来。这个过程可以使用符号!来完成。

var someNumber: Int?

someNumber =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合并,使用两个??符号连接在一起表示:如果存在值则获取当前值,如果不存在则获取给定的默认值,大致代码如下:

varsomeNumber: Int?

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

上述打代码大致等价于:

var someNumber:Int?

varnumber:Int

if let unwrapped:Int= someNumber {

number= unwrapped

}else{

number=0

}

swift-case和guard

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

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

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

{

case0?:print("长沙市木有程序猿类")

case(1..<1000)?:print("长沙市程序猿类竟然不到1000人")

case.Some(letx):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-else、if-let、swait-case、guard之中的一种对Optional返回的结果进行判断后再处理。

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

推荐阅读更多精彩内容