2.4 两种特殊的基本数据类型
Swift语言中还支持两种特殊的基本数据类型,分别是元组类型与可选值类型。元组在实际开发中十分常用,开发者使用元组可以创建出任意数据类型组合的自定义数据类型。而可选值类型是Swift语言的一大特点,通过可选值类型,Swift语言对数值为空进行了严格的把控。
2.4.1 元组
元组是Swift语言中重要数据类型之一,元组允许一些并不相关的类型进行自由组合成为新的集合类型。在Objective-C语言中并不支持元组这样的数据类型,这在很多时候会给开发者带来麻烦。类比生活中的一种情景,元组类型十分类似于日常生活中的套餐,现在各种服务业都有许多特色的套餐推出供顾客选择,方便为顾客提供一站式服务。元组提供的就是这样一种编程结构,试想一下,编程中会遇到这样一种情形,一个商品有名字和价格,使用元组可以很好地对这种商品类型进行模拟,示例如下:
//创建钢笔元组类型,其中有两种类型组成,字符串类型的名称和整数类型的价钱 var pen:(name:String, price:Int) = ("钢笔",2)
上面代码在创建元组类型的同时也将其中参数的名称进行了指定,即名称参数为name,价格参数为price,开发者可以使用这些参数名称来获取元组中各个参数的值,示例如下:
//获取pen变量名称 var name = pen.name //获取pen变量价格 var price = pen.price
开发者在创建元组时,也可以不指定元组中参数的名称,元组会自动为每个参数分配下标,下标值将从0开始依次递增,示例如下:
//不指定参数名称的元组 var car:(String, Int) = ("奔驰",2000000) //通过下标来取得元组中各个组成元素的值 var carName = car.0 var carPrice = car.1
元组实例被创建后,开发者也可以通过指定的变量或者常量来分解它,示例如下:
//不指定参数名称的元组 var car:(String, Int) = ("奔驰",2000000) //进行元组的分解 var (theName, thePrice) = car //此时theName变量被赋值为"奔驰"、thePrice变量被赋值为2000000 print(theName, thePrice)
上面代码将元组实例car中的各个组成元素分解到具体变量,有一点读者需要注意,分解后的变量必须与元组中的元素一一对应(个数相等),否则编译器就会报错。代码中使用的print()函数为打印输出函数,print()函数可以接收多个参数,将其以逗号分隔即可。有些时候,开发者可能并不需要获取某个元组实例中所有元素的值,这种情况下,开发者也可以将某些不需要获取的元素使用匿名的方式来接收,示例如下:
//不指定参数名称的元组 var car:(String, Int) = ("奔驰",2000000) //进行元组的分解 将Int型参数进行匿名 var (theName, _) = car //此时theName变量被赋值为"奔驰" print(theName)
在Swift语言中,常常使用符号“_”来表示匿名的概念,因此“_”也被称为匿名标识符。上面的代码实际上只分解出了元组car中的第一个元素(String类型)。
元组虽然使用起来十分方便,然而其只适用于简单数据的组合,对于结构复杂的数据,要采用结构体或者类来实现。
2.4.2 可选值类型
可选值类型(Optional类型)是Swift语言特有的一种类型。首先,Swift语言是一种十分强调类型安全的语言,开发者在使用到某个变量时,编译器会尽最大可能保证此变量的类型和值的明确性,保证减少编程中的不可控因素。然而在实际编程中,无论任何类型的变量都会遇到值为空的情况,在Objective-C语言中并没有机制来专门监控和管理为空值的变量,程序的运行安全性全部靠开发者手动控制。Swift语言中提供了一种包装的方式来对普通类型进行Optional包装,实现对空值情况的监控。
在Swift语言中,如果使用了一个没有进行赋值的变量,程序是会直接报错停止运行的。读者可能会想,如果一个变量在声明的时候没有赋初值,在后面的程序运行中就有可能被进行赋值,那么在使用时,应该怎么做呢?在Objective-C中,这个问题很好解决,只需要使用的时候,判断一下此变量是否为nil即可,那么在Swift中是否也可以这样做呢?使用如下代码来进行试验:
var obj:String if obj==nil { }
编写上面代码后,可以看到Xcode工具依然抛出了一个错误提示,其实在Swift语言中普通的类型是不允许为nil的,当然也就不可以与nil进行比较运算,这种机制极大地减小了代码的不可控性。如果一个变量在逻辑上可能为nil,则开发者需要将其包装为Optional类型,改写上面的代码如下:
var obj:String? if obj==nil { }
此时代码就可以正常运行了,分析上面代码,在声明obj变量的时候,这里将其声明成了String?类型,在普通类型后面添加符号“? ”,即可以将普通配型包装为Optional类型。
Optional类型不会独立存在,其总是会附着于某个具体的数据类型之上,具体的数据类型可以是基本数据类型,可以是结构体,也可以是类等。Optional类型只有两种值,读者可以将其理解为:
● 如果其附着类型对应的量值有具体的值,则其为具体值的包装。
● 如果其附着类型对应的量值没有具体的值,则其为nil。
举个例子,将Int类型的变量a进行Optional包装,则此时a的类型为Int?,如果对a进行了赋值,则可以通过拆包的方式获取a的Int类型值,如果没有对a进行赋值则a为nil。
Optional类型中的nil读者也可以理解为一种值。
Optional类型是对普通类型的一种包装,因此在使用的时候也需要对其进行拆包操作,拆包将使用到Swift中的操作符“! ”。“? ”与“! ”这两个操作符是很多Swift语言初学者的噩梦,如果读者理解了Optional类型,那么对这两个操作符的理解和使用也将容易许多。首先需要注意,“? ”符号可以出现在类型后面,也可以出现在实例后面,如果出现在类型后面,其代表的是此类型对应的Optional类型,如果出现在实例名后面,则代表的是可选链的调用,后面章节会有详细介绍。“! ”符号同样也可以出现在类型后面与实例后面,它出现在类型后面代表的是一种隐式解析的语法结构,后面章节会有介绍;出现在实例后面代表的是对Optional类型实例的拆包操作。示例如下:
//声明obj为String?类型 var obj:String? = "HS" //进行拆包操作 obj!
读者需要注意,在使用“! ”进行Optional值的拆包操作时,必须保证要拆包的值不为nil,否则程序运行会出错。可以在拆包前使用if语句进行安全判断,示例如下:
//声明obj为String?类型 var obj:String? = "HS" if obj ! = nil { obj! }
上面代码演示的编程结构在实际应用中十分广泛,因此Swift语言还提供了一种if-let语法结构来进行Optional类型值的绑定操作,可以将上面的结构改写如下:
var obj:String? = "HS" //进行if-let绑定 if let tmp = obj { print(tmp) }else{ obj = "HS" print(obj! ) }
上面的代码可以这样理解:如果obj有值,则if-let结构将创建一个临时常量tmp来接收obj拆包后的值,并且执行if为真时所对应的代码块,在执行的代码块中,开发者可以直接使用拆包后的obj值tmp。如果obj为nil,则会进入if为假的代码块中,开发者可以在else代码块中将obj重新赋值使用。这种if-let结构实际上完成了判断、拆包、绑定拆包后的值到临时常量3个过程。
if-let结构中也可以同时进行多个Optional类型值的绑定,之间用逗号隔开,示例如下:
//if-let多Optional值绑定 var obj1:Int? = 1 var obj2:Int? = 2 if let tmp1 = obj1, let tmp2 = obj2 { print(tmp1, tmp2) }
在同时进行多个Optional类型值的绑定时,只有所有Optional值都不为nil,绑定才会成功,代码执行才会进入if为真的代码块中。如果开发者需要在if语句的判断中添加更多业务逻辑,可以通过追加子句的方式来实现,示例如下:
//if-let多Optional值绑定 var obj1:Int? = 1 var obj2:Int? = 2 if let tmp1 = obj1, let tmp2 = obj2 , tmp1<tmp2 { print(tmp1, tmp2) }
上面的代码在obj1不为nil、obj2不为nil并且obj1所对应的拆包值小于obj2对应的拆包值的时候才会进入if判断为真的代码块,即打印绑定的tmp1与tmp2的值。
Optional值在Swift语言编程中的应用十分灵活,在以后的编程练习中,读者会逐步体会其中的奥妙。