原文:Swift Tutorial Part 2: Types and Operations

欢迎来到学习 Swift 的迷你系列的第二部分!

本章是榜首部分:表达式、变量和常量的续篇。我们主张你从本系列教程的榜首部分开端学习,以获得最大的收成。

现在是时分学习更多关于类型的知识了! 从形式上看,一个类型描绘了一系列值和能够对它们进行的操作。

在本教程中,你将学习怎样处理 Swift 编程言语中的不同类型。你将学习类型之间的转化,还将了解类型揣度,这将使你作为程序员的生活变得愈加简略。

终究,你将学习元组类型,它答应你创立由多个值组成的任何类型。

开端

有时,你会有一种格局的数据,需求将其转化为另一种格局。天真的尝试方法是这样的:

var integer: Int = 100
var decimal: Double = 12.5
integer = decimal

假如你试图这样做,Swift 会抱怨,并在第三行吐出一个过错:

Cannot assign value of type 'Double' to type 'Int'

有些编程言语并不那么严格,会自动进行这样的类型转化。经历标明,这种自动转化是软件发生过错的万恶之源,而且往往会危害功用。Swift 阻止你把一种类型的值赋给另一种类型,这就防止了上述问题。

记住,计算机依靠程序员来告知它们该怎样做。在 Swift 中,这包括对类型转化的清晰阐明。假如你想让转化发生,你有必要这样说。

你需求清晰地写出你想转化的类型,而不是简略地赋值。你能够这样做:

var integer: Int = 100
var decimal: Double = 12.5
integer = Int(decimal)

第三行的赋值句子现在清晰地告知 Swift,你想从原始类型 Double 转化到新类型 Int

在这种状况下,将小数点后的值分配给整数会导致精度的丢掉。整数变量终究的值是12,而不是12.5。这便是清晰的重要性。Swift 想保证你知道你在做什么,以及你或许会由于履行类型转化而终究丢掉数据。

操作混合类型

到目前为止,你只看到了独立作用于整数或 Double 类型的运算符。可是,假如你有一个整数,你想把它乘以一个浮点数呢?

或许你以为你能够这样做:

let hourlyRate: Double = 19.5
let hoursWorked: Int = 10
let totalCost: Double = hourlyRate * hoursWorked

假如你尝试这样做,你会在终究一行得到一个过错:

Binary operator '*' cannot be applied to operands of type 'Double' and 'Int'

这是由于,**在 Swift 中,你不能将 * 运算符应用于混合类型。这个规则也适用于其他算术运算符。**一开端或许会觉得很惊讶,但 Swift 是适当有协助的。

Swift 迫使你清晰阐明,当你想用 Int 乘以 Double 时,你想要表达的意图,由于结果只能是一种类型。你想让结果是 Int,在履行乘法之前将 Double 转化为 Int。或许你想让结果是一个 Double,在履行乘法之前将 Int 转化为 Double

在这个比如中,你期望结果是一个 Double 类型。你不想要一个 Int,由于在这种状况下,Swift 会把 hourlyRate 常数转化成 Int 来履行乘法,把它四舍五入到19,失去 Double 的精度。

你需求告知 Swift,你想让它把 hoursWorked 常量看作是一个 Double,像这样:

let hourlyRate: Double = 19.5
let hoursWorked: Int = 10
let totalCost: Double = hourlyRate * Double(hoursWorked)

现在,当 Swift 将每个操作数相乘时都是一个 Double,所以 totalCost 也是一个 Double 类型的数值。

类型揣度

到目前为止,每次你看到一个变量或常量的声明时,它都伴随着一个相关的类型,像这样:

let integer: Int = 42
let double: Double = 3.14159

你或许会问自己。“已然赋值的右侧现已是一个 Int 或一个 Double,为什么我一定要写上:IntDouble?” 能够必定的是,这是多余的;你不需求做太多的作业就能够看到这一点。

事实证明,Swift 的编译器也能揣度出这一点。它不需求你一直告知它类型——它能够自己想出来。这是经过一个叫做类型揣度的过程完结的。不是一切的编程言语都有这个功用,但 Swift 有,而且这是 Swift 言语的关键组成部分。

因而,你能够在大多数你看到类型的当地简略地放弃类型声明。

例如,考虑下面的常量声明:

let typeInferredInt = 42

有时,查看一个变量或常量的揣度类型是很有用的。你能够在 playground 中经过按住 Option 键并点击变量或常量的名称来完结这个使命。Xcode 将显现一个像这样的弹出窗口:

Swift 教程Part2:类型与操作

Xcode 经过给你供给声明来告知你揣度的类型,假如没有类型揣度,你将不得不运用这个声明。在这种状况下,该类型是 Int

它也适用于其他类型:

let typeInferredDouble = 3.14159

点击这个选项,能够看到以下内容:

Swift 教程Part2:类型与操作

类型揣度并不神奇。Swift 仅仅在做你的大脑很简略做到的作业。不运用类型揣度的编程言语往往会让人感觉很烦琐,由于你每次声明一个变量或常量时,都需求指定通常很明显的类型。

有时,你想界说一个常量或变量,并保证它是一个特定的类型,即使你要分配给它的是一个不同的类型。你在前面看到了怎样从一种类型转化到另一种类型。例如,考虑下面的状况:

let wantADouble = 3

在这儿,Swift 将 wantADouble 的类型揣度为 Int。但假如你想要的是 Double 呢?

你能够这样做:

let actuallyDouble = Double(3)

这与你之前看到的类型转化是相同的。

另一个挑选是根本不运用类型揣度,而是做以下作业:

let actuallyDouble: Double = 3

还有第三个选项,像这样:

let actuallyDouble = 3 as Double

这运用了一个你以前没有见过的新关键词,即 as,它也进行了一个类型转化。

注意:像 3 这样的字面值没有类型。只有在表达式中运用它们,或将它们赋值给一个常量或变量时,Swift 才会为它们揣度出一个类型。 一个不包括小数点的字面数字值也能够作为一个 Int 和一个 Double 运用。这便是为什么你被答应将数值3分配给常量actuallyDouble

含有小数点的数值不能是整数。这意味着你能够防止评论整个问题,假如你一开端就这样做:

let wantADouble = 3.0

Strings

数字在编程中是必不可少的,但它们并不是你在应用程序中需求处理的仅有数据类型。文本也是一种极为常见的数据类型,例如人的姓名、地址,乃至是一本书的文字。一切这些都是一个应用程序或许需求处理的文本的比如。

大多数计算机编程言语将文本存储在一种叫做 String 的数据类型中。本教程的这一部分向你介绍了字符串,首先向你介绍字符串概念的背景,然后向你展示怎样在 Swift 中运用它们。

计算机怎样表明字符串

计算机以为字符串是单个字符的调集。一切的代码,不管用什么编程言语,都能够简化为原始数字。字符串也不破例!

这听起来或许十分奇怪。字符怎样会是数字呢?从根本上说,计算机需求能够将一个字符翻译成计算机自己的言语,它经过给每个字符分配一个不同的数字来做到这一点。这就形成了一个从字符到数字的双向映射,被称为字符集。

Swift 教程Part2:类型与操作

当你在键盘上按下一个字符键时,你实际上是在向计算机传达该字符的数字。你的文字处理程序将这个数字转化成字符的图片,终究将这个图片呈现给你。

Unicode

在单独状况下,计算机能够自由挑选它喜爱的任何字符集映射。假如计算机想让字母a等于数字10,那就这样吧。可是,当计算机之间开端相互通信时,它们需求运用一个一起的字符集。假如两台计算机运用不同的字符集,那么,当一台计算机向另一台计算机传输字符串时,它们终究会以为这些字符串包括不同的字符。

多年来有几个规范,但最现代的规范是 Unicode。它界说了今天几乎一切计算机都运用的字符集映射。

你能够在其官方网站上阅览更多关于 Unicode 的信息,unicode.org。

考虑一下 cafe 这个词。Unicode 规范告知你,这个词的字母应该像这样被映射到数字上:

Swift 教程Part2:类型与操作

与每个字符相关的数字被称为码位。因而,在上面的比如中,c 运用码位99,a 运用码位97,以此类推。

当然,Unicode 不仅适用于英语中运用的简略拉丁字符,如 c、a、f 和 e,它还能够让你映射来自世界各地言语的字符。cafe 这个词来自法语,在法语中被写成 caf。Unicode 对这些字符的映射是这样的:

Swift 教程Part2:类型与操作

而这儿是一个运用汉字的比如(根据谷歌翻译,它的意思是 “计算机编程”):

Swift 教程Part2:类型与操作

你或许听说过 emojis,它是能够在你的文本中运用的小图片。事实上,这些图片仅仅普通的字符,也是由Unicode映射的。比如说:

Swift 教程Part2:类型与操作

这些仅仅两个字符。它们的码位是十分大的数字,但每个码位仍然仅仅一个码位。计算机以为它们与其他两个字符没有区别。

“emoji”一词来自日语:“e”表明图片,“moji”表明字符。

Swift 中的字符串

Swift,像任何一门完善的编程言语相同,能够直接处理字符和字符串。它分别经过数据类型 CharacterString 来完结。在本节中,你将了解这些数据类型以及怎样运用它们。

Character 和 String

Character 类型能够存储单个字符。如:

let characterA: Character = "a"

它能够存储任何字符–乃至是一个表情符号:

let characterDog: Character = ""

可是这种数据类型被设计成只能存储单一字符。另一方面,String 数据类型能够存储多个字符。如:

let stringDog: String = "Dog"

这个表达式的右边便是所谓的字符串字面量,它是 Swift 的语法,用来表明一个字符串。

当然,类型揣度在这儿也适用。假如你去掉上面声明中的类型,那么 Swift 就会做正确的作业,让 stringDog 成为一个 String 常量:

let stringDog = "Dog" // Inferred to be of type String

在 Swift 中没有所谓的字符字面量。一个字符仅仅一个长度为1的字符串。可是,Swift 揣度任何字符串字面的类型都是 String,所以假如你想用 Character 来替代,你有必要清晰类型。

字符串拼接

你能做的远不止创立简略的字符串。有时,你需求操作一个字符串,一个常见的办法是将其与另一个字符串结合。

在 Swift 中,你能够用一个适当简略的办法来做这件事:运用加法运算符。就像你能够加数字相同,你也能够加字符串:

var message = "Hello" + " my name is "
let name = "Lorenzo"
message += name // "Hello my name is Lorenzo"

你需求将 message 声明为一个变量,而不是一个常量,由于你想修改它。你能够像榜首行那样把字符串字面符号加在一起,你也能够像终究一行那样把字符串变量或常量加在一起。

也能够将字符添加到一个字符串中。可是,Swift 对类型的严格要求意味着你在这样做时有必要清晰,就像你在处理数字时有必要清晰,假如一个是 Int,另一个是 Double。

要向字符串中添加一个 character 字符,你要这样做:

let exclamationMark: Character = "!"
message += String(exclamationMark) // "Hello my name is Lorenzo!"

经过这段代码,你在将字符添加到音讯中之前清晰地将其转化为字符串。

字符串插值

你也能够经过运用插值来树立一个字符串,这是一种特别的 Swift 语法,让你以一种易于阅览的方法创立一个字符串:

let name = "Lorenzo"
let messageInOne = "Hello my name is (name)!" // "Hello my name is Lorenzo!"

上面的比如比上一节的比如可读性强多了。这是对字符串字面语法的扩展,即用其他值替换字符串的某些部分。你把你想给字符串的值放在小括号里,前面加一个反斜杠。

这种语法的作业方法与从其他数据类型(如数字)树立字符串的方法相同:

let oneThird = 1.0 / 3.0
let oneThirdLongString = "One third is (oneThird) as a decimal."

这儿,你在插值中运用了一个 Double。在这段代码的终究,你的 oneThirdLongString 常量将包括以下内容:

One third is 0.3333333333333333 as a decimal.

当然,实际上需求无限的字符来表明小数的三分之一,由于它是一个重复的小数。用 Double 进行字符串插值,你没有办法操控结果字符串的精度。

这是运用字符串插值的一个不幸的结果;它运用起来很简略,但没有供给自界说输出的能力。

多行字符串

Swift 有一个很好的办法来表达包括多行的字符串。当你需求把一个很长的字符串放在你的代码中时,这或许是适当有用的。

你能够这样做:

let bigString = """
  You can have a string
  that contains multiple
  lines
  by
  doing this.
  """
print(bigString)

三个双引号标志着这是一个多行字符串。便利的是,榜首个和终究一个新行并不成为字符串的一部分。这使得它愈加灵敏,由于你不必将这三个双引号与字符串放在同一行。

在上面的比如中,它将打印如下:

You can have a string
that contains multiple
lines
by
doing this.

请注意,多行字符串字面中的两个空格被从结果中剥离出来。Swift 看的是终究三个双引号行的前导空格的数量。以此为基准,Swift要求它上面的一切行至少有这么多的空格,这样它就能够从每行中删除。这能够让你用美丽的缩进来格局化你的代码,而不影响输出。

元组

有时,数据是成对的或三倍的。这方面的一个比如是二维网格上的一对(x,y)坐标。同样地,三维网格上的一组坐标由一个x值、一个y值和一个z值组成。

在 Swift 中,你能够经过运用元组以一种十分简略的方法来表明这些相关的数据。

**元组是一种类型,表明由任何类型的一个以上的值组成的数据。**你能够在你的元组中拥有任意多的值。例如,你能够界说一对二维坐标,其中每个轴的值是一个整数,像这样:

let coordinates: (Int, Int) = (2, 3)

坐标的类型是一个包括两个 Int 值的元组。元组内的值的类型,在这种状况下是 Int,用逗号离隔,周围有括号。创立元组的代码基本相同,每个值都由逗号离隔,并由圆括号围住。

类型揣度也能够揣度出元组的类型:

let coordinatesInferred = (2, 3) // Inferred to be of type (Int, Int)

你能够类似地创立一个 Double 值的元组,像这样:

let coordinatesDoubles = (2.1, 3.5) // Inferred to be of type (Double, Double)

或许你能够混合和匹配组成元组的类型,像这样:

let coordinatesMixed = (2.1, 3) // Inferred to be of type (Double, Int)

而这儿是怎样访问元组内的数据:

let coordinates = (2, 3)
let x1 = coordinates.0
let y1 = coordinates.1

**你能够经过元组中的下标索引来引证元组中的每个项目,从零开端。**因而,在这个比如中,x1等于2,y1等于3。

从零开端是计算机编程中的一种常见做法,被称为零点索引。 在前面的比如中,或许不是很明显,索引0对应的榜首个值是x坐标,索引1对应的第二个值是y坐标。这就再次阐明了为什么总是以防止混杂的方法来命名你的变量是很重要的。

走运的是,**Swift 答应你对元组的各个部分进行命名,你能够清晰每个部分代表什么。**比如说:

let coordinatesNamed = (x: 2, y: 3) // Inferred to be of type (x: Int, y: Int)

在这儿,代码注释了 coordinatesNamed 的值,以包括元组中每个部分的标签。

然后,当你需求访问元组的每个部分时,你能够经过它的名字来访问它:

let x2 = coordinatesNamed.x
let y2 = coordinatesNamed.y

这就更清楚、更简略了解了。更多的时分,命名你的元组的组成部分是有协助的。

假如你想同时访问元组的多个部分,就像上面的比如相同,你也能够运用速记语法来使之更简略:

let coordinates3D = (x: 2, y: 3, z: 1)
let (x3, y3, z3) = coordinates3D

这就声明了三个新的常数,x3、y3和z3,并顺次将元组的每一部分分配给它们。该代码等同于以下内容:

let coordinates3D = (x: 2, y: 3, z: 1)
let x3 = coordinates3D.x
let y3 = coordinates3D.y
let z3 = coordinates3D.z

假如你想疏忽元组中的某个元素,你能够用下划线替换声明中的相应部分。例如,假如你在进行二维计算,并想疏忽坐标3D的Z坐标,那么你能够写如下:

let (x4, y4, _) = coordinates3D

这一行代码只声明了 x4y4 变量。_ 是特别字符,仅仅意味着你暂时疏忽这部分。

注意:你会发现,你能够在整个 Swift 中运用下划线–也叫通配符–来疏忽一个值。

一大堆数字类型

你一直在用 Int 来表明整数。一个 Int 在大多数现代硬件上用 64 位表明,而在较老的–或资源较紧张的–体系上用 32 位表明。Swift 供给了更多运用不同存储量的数字类型。关于整数,你能够运用显式符号类型 Int8Int16Int32 Int64。这些类型分别耗费 1、2、4 和 8 字节的存储空间。这些类型中的每一个都运用 1 位来表明符号。

假如你只处理非负值,有一组清晰的无符号类型能够运用。这些类型包括 UInt8, UInt16, UInt32 UInt64。尽管你不能用这些类型来表明负值,但额定的1位能够让你表明比有符号类型大一倍的值。

下面是对不同整数类型及其存储特性的总结。**大多数状况下,你只想运用 Int。**假如你的代码与另一个运用这些更精确巨细的软件进行交互,或许你需求对存储巨细进行优化,这些就变得很有用。

Swift 教程Part2:类型与操作

你一直在运用 Double 来表明小数。Swift 供给了一个 Float 类型,它的范围和精度都比 Double 小,但需求一半的存储。现代硬件现已为 Double 进行了优化,所以除非你有充分的理由,否则你应该运用它。

Swift 教程Part2:类型与操作

大多数时分,你只需求运用 Int 和 Double 来表明数字,可是,每隔一段时间,你或许会遇到其他类型。你现已知道怎样处理它们了。例如,假定你需求将一个Int16与一个UInt8和一个Int32加在一起。你能够这样做。

let a: Int16 = 12
let b: UInt8 = 255
let c: Int32 = -100000
let answer = Int(a) + Int(b) + Int(c) // Answer is an Int

暗地原理窥视:Protocols

尽管有十几种不同的数字类型,但它们都适当简略了解和运用。这是由于它们都大致上支持相同的操作。换句话说,一旦你知道怎样运用 Int,运用任何一种类型都是十分简略的。

Swift 真实伟大的功用之一是它怎样运用所谓的协议来正式确认类型通用性的主意。经过学习一个协议,你马上就能了解运用该协议的整个类型宗族是怎样作业的。

以 Integers 整数为例,其功用能够这样图示:

Swift 教程Part2:类型与操作

箭头表明遵循(有时称为选用)一个协议。尽管这张图没有显现整数类型符合的一切协议,但它让你了解作业是怎样组织的。

Swift 是榜首个基于协议的言语。当你开端了解这些类型所依据的协议时,你就能够开端以其他言语无法完结的方法使用这个体系。

何去何从?

你能够运用本教程顶部或底部的下载材料按钮下载终究的操场。为了进步你的 Swift 技术,你会发现一些小练习要完结。假如你被卡住了,或许你需求一些协助,能够随时使用配套的解决方案。

在本教程中,你现已了解到类型是编程的一个基本部分。它们答应你正确地存储你的数据。你在这儿还看到了一些类型,包括字符串和图元,以及一堆数字类型。

鄙人一部分,你将学习布尔逻辑和简略的操控流。继续阅览第三部分:流操控,以继续这个系列。

假如有任何问题或定见,请鄙人面的评论中告知我们。

本教程取自《Swift学徒》第四版第三章,可在 raywenderlich.com 商铺购买。

请看一下,并让我们知道你的主意!