作者刘文涛
转载请注明出处
前言
由于最近工作比较忙,一直没时间跟新博客。最近项目告一段落了,继续开始我的Swift学习之路。
数值型字面量
整数字面量可以被写作:
一个十进制数,没有前缀
一个二进制数,前缀 0b
一个八进制数,前缀 0o
一个十六进制数,前缀 0x
下面的所有整数字面量的十进制都是 17:
|
|
数值型类型转换
通常来讲,即使代码中的整数常量和变量已知非负,也请使用 Int 类型。总是使用默认的整数类型可以保证你的整数常量和变量可以直接被复用并且可以匹配整数类字面量的类型推断。
只有在必要的时候才使用其他整数类型,比如要处理外部额长度明确的数据或者为了优化性能、内存占用等等。使用显示指定长度的类型可以及时发现值溢出并且可以暗示正在处理特殊数据。
整数转换
不同整数类型的变量和常量可以存储不同范围的数字。Int8 类型的常量或者变量可以存储的数字范围是 -128~127,而 UInt8 类型能存储的数字范围是 0~255 。如果数字超出了常量或者变量可存储的范围,编译的时候会报错:
|
|
由于每种整数类型都可以存储不同范围的值,所以你必须根据不同情况选择性使用数值型类型转换。这种选择性使用的方式,可以预防隐式转换的错误并让你的代码中的类型转换意图变得清晰。
要将一种数字类型转换成另一种,你要用当前值来初始化一个期望类型的新数字,这个数字的类型就是你的目标类型。
|
|
因为两个常量的类型不一样 不能直接相加,所以要调用 UInt16(one) 来创建一个新的 UInt16 数字并用 one 的值来初始化,然后使用这个新数字来计算。
SomeType(ofInitialValue) 是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部, UInt16 有一个 构造器,可以接受一个 UInt8 类型的值,所以这个构造器可以用现有的 UInt8 来创建一个新的 UInt16 。注 意,你并不能传入任意类型的值,只能传入 UInt16 内部有对应构造器的值。不过你可以扩展现有的类型来让它 可以接收其他类型的值(包括自定义类型)。
整数和浮点数转换
整数和浮点数的转换必须显式指定类型:
|
|
这个例子中,常量 three 的值被用来创建一个 Double 类型的值,所以加号两边的数类型需相同。如果不进行转换,两者无法相加。
浮点数到整数的反向转换同样行,整数类型可以用 Double 或者 Float 类型来初始化。
类型别名
类型别名(type aliases)就是给现有类型定义另一个名字。你可以使用 typealias
关键字来定义类型别名。
当你想给现有类型起一个更有意义的名字时,类型别名非常有用。假设你正在处理特定长度的外部资源数据:
|
|
定义了一个类型别名之后,你可以在任何使用原始名的地方使用别名:
|
|
本例中,AudioSample 被定义为 UInt16 的一个别名。因为他是别名。AudioSample.min实际上是 UInt16.min ,所以会给 maxAmplitudeFound 赋一个初值 0。
布尔值
Swift有一个基本的布尔(Boolean)类型,叫做 Bool 。布尔值指逻辑上的值,因为它们只能是真或者假。Swift有两个布尔常量, true 和 false 。
|
|
t 和 f 的类型会被推断为 Bool,因为它们的初值是布尔字面量。就像之前提到的 Int 和 Double 一样,如果你创建变量的时候给他们赋值 true 或者 false ,那你不需要将常量或者变量声明为 Bool 类型。初始化常量或者变量的时候如果所赋的值类型已知,就可以触发类型推断,折让Swift的代码更简洁并且可读性更高。
元组
元组(tuples)可以把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型。
下面这个例子中, (404, "Not Found")
是一个描述 HTTP 状态码(HTTP status code)的元组。HTTP 状态码是 当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个 404 Not Found
状态码。
|
|
(404, "Not Found")
元组把一个 Int 值和一个 String 值组合起来表示 HTTP 状态码的两个部分,这个元组可以被描述为 “一个类型为(Int, String)的元组”。
你可以把任意顺序的类型组合成一个元组,这个元组可以包含所有类型。只要你想,你可以创建一个类型为(Int, Int, Int)或者(String, Bool)或者其他任何你想要的组合的元组。
你可以将一个元组的内容分解(decompose)成单独的常量和变量,然后你就可以正常使用它们了:
|
|
如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线(_)标记:
|
|
此外,你还可以通过下标来访问元组中的单个元素,下标从 0 开始:
|
|
你可以在定义元组的时候给单个元素命名:
|
|
给元组中的元素命名后,你可以通过名字来获取这些元素的值:
|
|
作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个 (Int, String) 元组来描述是否 获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。
注意:元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。
可选类型
使用可选类型(optionals)来处理值可能缺失的情况。可选类型表示:
有值, 等于 x
或者
没有值
注意:C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一 个对象要不返回 nil , nil 表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法一般会返回一个特殊值(比如 NSNotFoun
d )来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而,Swift 的可选类型可以让 你暗示任意类型的值缺失,并不需要一个特殊值。
来看一个例子。Swift的 Int 类型有一种构造器,作用是将一个 String 值转换成一个 Int 值。然而,并不是所有的字符串都可以抓换成一个整数。字符串 “123” 可以被转换成数字 123 ,但是字符串 “hello, world” 不行。
下面的例子使用这种构造器来尝试将一个 String 转换成 Int:
|
|
因为该构造器可能会失败,所以它返回一个可选类型(optional) Int,而不是一个 Int。一个可选的 Int 被写作 Int? 而不是 Int 。问号暗示包含的值是可选类型,也就是说可能包含 Int 值也可能不包含值。(不能包含其他任何值,比如 Bool值或者 String值。只能是 Int 或者什么也没有。)
nil
你可以给可选变量赋值为 nil 来表示它没有值:
|
|
注意: nil 不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
如果你声明了一个可选常量或者变量但是没有赋值,它们会自动被设置为 nil。
注意: Swift 的 nil 和 OC 中的 nil 并不一样。在 OC 中,nil 是一个指向不存在对象的指针。在 Swift 中,nil 不是指针–它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 nil,不只是对象类型。
if语句以及强制解析
你可以使用 if
语句和 nil
比较来判断一个可选值是否包含值。如果可选类型有值,它将不等于 nil
:
|
|
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(!
)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的强制解析(forced unwrapping):
|
|
可选绑定
使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在 if
和 while
语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。
你可以像下面那样在 if 语句中写一个可选绑定:
|
|
这段代码可以被理解为:
“如果 Int(possibleNumber)
返回的可选 Int
包含一个值,创建一个叫做 actualNumber
的新常量并将可选 包含的值赋给它。”
如果转换成功, actualNumber
常量可以在 if 语句的第一个分支中使用。它已经被可选类型 包含的 值初始化 过,所以不需要再使用 !
后缀来获取它的值。在这个例子中,actualNumber
只被用来输出转换结果。
你可以在可选绑定中使用常量和变量。如果你想在if语句的第一个分支中操作 actualNumber
的值,你可以改 成 if var actualNumber
,这样可选类型包含的值就会被赋给一个变量而非常量。
隐式解析可选类型
如上所述,可选类型暗示了常量或者变量可以”没有值“。可选可以通过 if
语句来判断是否有值,如果有值的话就可以通过可选绑定来解析值。
有的时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总是会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。
这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(String?
)改为感叹号(String!
)来声明一个隐式解析可选类型。
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中。
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都是用解析来获取可选值。下面的例子展示了可选类型 String
和隐式解析可选类型 String
之间的区别:
|
|
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。
注意:
如果一个变量之后可能变成 nil 的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否 是 nil 的话,请使用普通可选类型。
错误处理
你可以使用错误处理(error handling)来应对程序执行中可能会遇到的错误条件。
相对于可选中运用值的存在与缺失来表达函数的成功与失败,错误处理可以推断失败的原因,并传播至程序的其他部分。
当一个函数遇到错误条件,它能报错。调用函数的地方能抛出错误消息并合理处理。
|
|
一个函数可以通过在声明中添加 throws
关键字来抛出错误消息。当你的函数能抛出错误消息时,你应该在表达式中前置 try
关键字。
|
|
一个 do
语句创建了一个新的包含作用域,使得错误能被传播到一个或多个 catch
从句。
断言
可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能没办法继续执行。这时,你可以在你的代码中触发一个 断言(asse rtion) 来结束代码运行并通过调试来找到值缺失的原因。
如果你的代码在调试环境下触发了一个断言,比如你在 Xcode 中构建并运行一个应用,你可以清楚地看到不合法 的状态发生在哪里并检查断言被触发时你的应用的状态。此外,断言允许你附加一条调试信息。
你可以使用全局 assert(_:_:file:line:)
函数来写一个断言。向这个函数传入一个结果为 true 或者 false 的表达式以及一条信息,当表达式的结果为 false 的时候这条信息会被显示:
|
|
在这个例子中,只有 age >= 0
为 true 的时候,即 age 的值非负的时候,代码才会继续执行。如果 age 的值是负数,就像代码中那样, age >= 0
为 false ,断言被触发,终止应用。
注意:
当代码使用优化编译的时候,断言将会被禁用,例如在 Xcode 中,使用默认的 target Release 配置选项来 build 时,断言会被禁用。
何时使用断言
当条件可能为假时使用断言,但是最终一定要保证条件为真,这样你的代码才能继续运行。断言的适用情景:
整数类型的下标索引被传入一个自定义下标实现,但是下标索引值可能太小或者太大。
需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。
一个可选值现在是 nil ,但是后面的代码运行需要一个非 nil 值。
注意:
断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。