变量

基本操作员

一个运营商是一个特殊的符号,或者你使用来检查,更改或合并值的短语。例如,加法运算符(+)添加两个数字,如in let i = 1 + 2,逻辑AND运算符(&&)组合两个布尔值,如in if enteredDoorCode && passedRetinaScan

Swift支持大多数标准C运算符,并改进了几种消除常见编码错误的功能。赋值运算符(=)不会返回值,以防止在等于运算符(==)时错误地使用该值。算术运算符(+-*/%等等)检测和禁止值溢出,与变得比存储它们的类型的所允许的值的范围更大或更小数目的工作时避免意外的结果。您可以通过使用雨燕的溢出运营商选择在价值溢出行为,如在溢出运营商

Swift还提供了在C中没有的范围运算符,比如a..<ba...b,作为表达一系列值的快捷方式。

本章介绍Swift中的常用操作符。高级操作符涵盖了Swift的高级操作符,并描述了如何定义自己的自定义操作符并为您自定义的类型实现标准操作符。

术语

运算符是一元,二元或三元运算符:

  • 一元运算符在单个目标上运行(例如-a)。一元前缀运算符紧挨着它们的目标(例如!b)出现,而一元后缀运算符出现在它们的目标之后(例如c!)。

  • 二元操作符运行在两个目标上(如2 + 3),并且是中缀,因为它们出现在两个目标之间。

  • 三元运营商在三个目标上运营。像C一样,Swift只有一个三元运算符,即三元条件运算符(a ? b : c)。

操作符影响的值是操作数。在表达式中1 + 2+符号是二元运算符,它的两个操作数是值12

赋值运算操作

赋值运算符a = b)初始化或更新的价值a与价值b

  1. let b = 10
  2. var a = 5
  3. a = b
  4. // a is now equal to 10

如果赋值的右侧是一个具有多个值的元组,则其元素可以一次分解为多个常量或变量:

  1. let (x, y) = (1, 2)

  2. // x is equal to 1, and y is equal to 2

  3. print(x) //1

与C和Objective-C中的赋值运算符不同,Swift中的赋值运算符本身不返回值。以下声明无效:

  1. if x = y {
  2. // This is not valid, because x = y does not return a value.
  3. }

此功能可以防止赋值运算=符(==)在实际意图等于运算符()时被意外使用。通过使if x = y无效,Swift可以帮助您避免代码中出现这些类型的错误。

算术运算符

Swift支持所有数字类型的四个标准算术运算符

  • 加法(+

  • 减法(-

  • 乘法(*

  • 司(/

  1. 1 + 2 // equals 3
  2. 5 - 3 // equals 2
  3. 2 * 3 // equals 6
  4. 10.0 / 2.5 // equals 4.0

与C和Objective-C中的算术运算符不同,Swift算术运算符默认情况下不允许值溢出。你可以通过使用Swift的溢出操作符(比如a &+ b)来选择赋值溢出行为。请参阅溢出操作符

String连接 操作还支持连接:

  1. "hello, " + "world" // equals "hello, world"

剩余操作员

余运算符a % b)的作品如何的好几倍b将适合的内部a,并返回剩下值(被称为剩余部分)。

注意

余数运算符(%)在其他语言中也称为模运算符。然而,它在Swift中对负数的行为意味着严格地说,它是一个余数而不是模操作。

以下是剩余操作符的工作方式。为了计算9 % 4,你首先要计算出有多少个4s可以放入里面9

<figure class="figure" style="font-size: 0.8rem;">[图片上传失败...(image-3b7ddc-1531103258569)]

</figure>

你可以放入两个4s 9,剩下的就是1(以橙色显示)。

在Swift中,这会写成:

  1. 9 % 4 // equals 1

为了确定答案a % b%操作员计算下面的公式并remainder作为其输出返回:

a=(bx some multiplier)+remainder

里面some multiplier最大的倍数是 哪里? b``a

插入94这个公式得到:

9=(4x 2)+1

计算余数为负值时应用同样的方法a

  1. -9 % 4 // equals -1

插入-94代入公式得到:

-9=(4x -2)+-1

给出一个余数值-1

b负号的值将忽略 符号b。这意味着a % ba % -b始终给出相同的答案。

一元减法运算符

数值的符号可以使用前缀-(称为一元减运算符)来切换:

  1. let three = 3
  2. let minusThree = -three // minusThree equals -3
  3. let plusThree = -minusThree // plusThree equals 3, or "minus minus three"

一元减号运算符(-)直接位于运算值的前面,没有任何空格。

一元加运算符

一元加运算+)只返回其所操作的价值,没有任何变化:

  1. let minusSix = -6
  2. let alsoMinusSix = +minusSix // alsoMinusSix equals -6

虽然一元加运算符实际上并没有做任何事情,但是在使用一元减运算符来表示负数时,也可以使用它来在代码中提供正数的对称性。

复合分配算子

和C一样,Swift提供了复合赋值操作符,它将assignment(=)与另一个操作结合在一起。一个例子是附加赋值运算符+=):

  1. var a = 1
  2. a += 2
  3. // a is now equal to 3

这个表达方式a += 2是速记的a = a + 2。实际上,添加和分配组合为一个运算符,可同时执行两个任务。

注意

复合赋值运算符不返回值。例如,你不能写let b = a += 2

有关Swift标准库提供的运算符的信息,请参阅运算符声明

比较运算符

Swift支持所有标准的C 比较运算符

  • 等于(a == b

  • 不等于(a != b

  • 大于(a > b

  • 小于(a < b

  • 大于或等于(a >= b

  • 小于或等于(a <= b

注意

Swift还提供了两个身份运算符===!==),用于测试两个对象引用是否都引用同一个对象实例。有关更多信息,请参阅类和结构

每个比较运算符都会返回一个Bool值来指示该语句是否为真:

  1. 1 == 1 // true because 1 is equal to 1
  2. 2 != 1 // true because 2 is not equal to 1
  3. 2 > 1 // true because 2 is greater than 1
  4. 1 < 2 // true because 1 is less than 2
  5. 1 >= 1 // true because 1 is greater than or equal to 1
  6. 2 <= 1 // false because 2 is not less than or equal to 1

比较运算符通常用于条件语句中,如if语句:

  1. let name = "world"
  2. if name == "world" {
  3. print("hello, world")
  4. } else {
  5. print("I'm sorry \(name), but I don't recognize you")
  6. }
  7. // Prints "hello, world", because name is indeed equal to "world".

有关该if声明的更多信息,请参阅控制流程

你可以比较两个元组,如果它们具有相同的类型和相同数量的值。元组从左到右进行比较,一次一个值,直到比较发现两个不相等的值。这两个值进行比较,比较的结果决定了元组比较的总体结果。如果所有元素都相等,那么元组本身是相等的。例如:

  1. (1, "zebra") < (2, "apple") // true because 1 is less than 2; "zebra" and "apple" are not compared
  2. (3, "apple") < (3, "bird") // true because 3 is equal to 3, and "apple" is less than "bird"
  3. (4, "dog") == (4, "dog") // true because 4 is equal to 4, and "dog" is equal to "dog"

在上面的示例中,您可以在第一行看到从左到右的比较行为。因为1小于2(1, "zebra")被认为小于(2, "apple"),不管元组中的任何其他值如何。没关系,"zebra"不小于"apple",因为比较已经由元组的第一个元素决定了。然而,当元组的第一个元素是相同的,他们的第二个元素进行比较 - 这是发生在第二和第三行。

只有当操作符可以应用于各个元组中的每个值时,元组才可以与给定的操作符进行比较。例如,这表现在下面的代码,就可以比较类型的两个元组(String, Int),因为这两个StringInt值可以使用进行比较<运算符。相比之下,两个元组类型(String, Bool)不能与<运算符进行比较,因为<运算符不能应用于Bool值。

  1. ("blue", -1) < ("purple", 1) // OK, evaluates to true
  2. ("blue", false) < ("purple", true) // Error because < can't compare Boolean values

注意

Swift标准库包含元组少于7个元组的元组比较运算符。要比较具有七个或更多元素的元组,您必须自己实现比较运算符。

三元条件运算符

所述三元条件算子是由三个部分组成,这需要形式的特殊操作question ? answer1 : answer2。这是根据是否question为真来评估两个表达式之一的捷径。如果question属实,则评估answer1并返回其值; 否则,它会评估answer2并返回其值。

三元条件运算符是下面代码的简写:

  1. if question {
  2. answer1
  3. } else {
  4. answer2
  5. }

以下是一个计算表格行高度的示例。如果该行有标题,则行高应比内容高度高50个点;如果该行没有标题,则行高应高20个点:

  1. let contentHeight = 40
  2. let hasHeader = true
  3. let rowHeight = contentHeight + (hasHeader ? 50 : 20)
  4. // rowHeight is equal to 90

上面的例子是下面代码的简写:

  1. let contentHeight = 40
  2. let hasHeader = true
  3. let rowHeight: Int
  4. if hasHeader {
  5. rowHeight = contentHeight + 50
  6. } else {
  7. rowHeight = contentHeight + 20
  8. }
  9. // rowHeight is equal to 90

第一个例子使用三元条件运算符意味着rowHeight可以在单行代码上设置正确的值,这比第二个例子中使用的代码更简洁。

三元条件运算符为决定要考虑哪两个表达式提供了有效的速记。但是,请谨慎使用三元条件运算符。如果过度使用,它的简洁会导致难以阅读的代码。避免将三元条件运算符的多个实例组合成一个复合语句。

无合并操作员

零-合并运算符a ?? b)进行解包的可选a,如果它包含一个值,或者返回一个默认值b,如果anil。表达式a总是可选的类型。表达式b必须与存储在里面的类型匹配a

nil-coalescing运算符是下面代码的简写:

  1. a != nil ? a! : b

上面的代码使用三元条件操作和强制解包(a!)来访问内部包裹的值aa是不nil,并返回b,否则。nil-coalescing运算符提供了一种更简洁的方式来以简明易懂的形式封装这个条件检查和解包。

注意

如果值为anil值,b则不评估值。这就是所谓的短路评估

下面的示例使用nil-coalescing运算符在默认颜色名称和可选的用户定义颜色名称之间进行选择:

  1. let defaultColorName = "red"

  2. var userDefinedColorName: String? // defaults to nil

  3. var colorNameToUse = userDefinedColorName ?? defaultColorName

  4. // userDefinedColorName is nil, so colorNameToUse is set to the default of "red"

userDefinedColorName变量被定义为可选的String,默认值为nil。由于userDefinedColorName是可选类型,因此您可以使用nil-coalescing运算符来考虑其值。在上面的示例中,运算符用于确定String调用变量的初始值colorNameToUse。因为userDefinedColorNamenil,表达式userDefinedColorName ?? defaultColorName返回值defaultColorName"red"

如果您将一个非nil值赋予userDefinedColorName并再次执行nil-coalescing操作符检查,则将userDefinedColorName使用内部包装的值而不是缺省值:

  1. userDefinedColorName = "green"
  2. colorNameToUse = userDefinedColorName ?? defaultColorName
  3. // userDefinedColorName is not nil, so colorNameToUse is set to "green"

范围运算符

Swift包含几个范围运算符,它们是表示一系列值的捷径。

关闭范围操作员

封闭范围操作符a...b)限定了从运行范围ab,并且包括这些值ab。值a不得大于b

在遍历希望使用所有值的范围(例如使用for- in循环)时,闭范围运算符很有用:

  1. for index in 1...5 {
  2. print("\(index) times 5 is \(index * 5)")
  3. }
  4. // 1 times 5 is 5
  5. // 2 times 5 is 10
  6. // 3 times 5 is 15
  7. // 4 times 5 is 20
  8. // 5 times 5 is 25

有关for- in循环的更多信息,请参阅控制流程

半开放范围运算符

所述半开区间运算符a..<b)限定了从运行范围ab,但不包括b。据说它是半开放的,因为它包含了它的第一个值,但不包含它的最终值。与封闭范围操作符一样,值a不得大于b。如果值a等于b,则结果范围将为空。

当您使用基于零的列表(例如数组)时,半开范围特别有用,可以计算(但不包括)列表长度:

  1. let names = ["Anna", "Alex", "Brian", "Jack"]
  2. let count = names.count
  3. for i in 0..<count {
  4. print("Person \(i + 1) is called \(names[i])")
  5. }
  6. // Person 1 is called Anna
  7. // Person 2 is called Alex
  8. // Person 3 is called Brian
  9. // Person 4 is called Jack

请注意,该数组包含四个项目,但0..<count只计算3(数组中最后一项的索引),因为它是半开范围。有关数组的更多信息,请参阅数组

单面范围

闭范围运算符有一种替代形式,用于在一个方向上尽可能延续的范围 - 例如,范围包括从索引2到数组末尾的所有数组元素。在这些情况下,可以省略范围运算符一侧的值。这种范围被称为单边范围,因为操作员只有一方的价值。例如:

  1. for name in names[2...] {

  2. print(name)

  3. }

  4. // Brian

  5. // Jack

  6. for name in names[...2] {

  7. print(name)

  8. }

  9. // Anna

  10. // Alex

  11. // Brian

半开范围的操作符也具有只写入其最终值的单面形式。就像在双方包含一个值时一样,最终的值不是该范围的一部分。例如:

  1. for name in names[..<2] {
  2. print(name)
  3. }
  4. // Anna
  5. // Alex

片面范围可以用在其他情况下,而不仅仅用于下标。您不能迭代忽略第一个值的单侧范围,因为尚不清楚应在哪里开始迭代。您可以遍历一个忽略其最终值的单侧范围; 但是,由于范围无限期地继续,因此请确保为循环添加明确的结束条件。您还可以检查单侧范围是否包含特定值,如下面的代码所示。

  1. let range = ...5
  2. range.contains(7) // false
  3. range.contains(4) // true
  4. range.contains(-1) // true

逻辑运算符

逻辑运算符修改或组合布尔逻辑值truefalse。Swift支持基于C语言中的三种标准逻辑运算符:

  • 逻辑NOT(!a

  • 逻辑与(a && b

  • 逻辑或(a || b

逻辑NOT运算符

逻辑非运算符!a)反转一个布尔值,使得true成为false,和falsetrue

逻辑NOT运算符是前缀运算符,并且在其运行的值之前立即出现,没有任何空格。它可以被解读为“不a”,如下例所示:

  1. let allowedEntry = false
  2. if !allowedEntry {
  3. print("ACCESS DENIED")
  4. }
  5. // Prints "ACCESS DENIED"

该短语if !allowedEntry可以被理解为“如果不允许进入”。后续行仅在“不允许进入”为真时才被执行; 那就是,如果allowedEntryfalse

正如在这个例子中,仔细选择布尔常量和变量名称可以帮助保持代码的可读性和简洁性,同时避免双重否定或混淆逻辑语句。

逻辑AND运算符

逻辑AND运算符a && b)创建逻辑表达式,其中这两个值必须true为整体表达也有true

如果任何一个值都是false,则整体表达式也将是false。事实上,如果第一个值是false第二个值,那么甚至不会评估第二个值,因为它不可能使整体表达式等于true。这就是所谓的短路评估

这个例子考虑两个Bool值,并且只允许访问如果价值观true

  1. let enteredDoorCode = true
  2. let passedRetinaScan = false
  3. if enteredDoorCode && passedRetinaScan {
  4. print("Welcome!")
  5. } else {
  6. print("ACCESS DENIED")
  7. }
  8. // Prints "ACCESS DENIED"

逻辑OR运算符

逻辑OR运算符a || b)是来自两个相邻管字符制成中缀运算符。您可以使用它来创建逻辑表达式,其中只有一个值必须true用于整体表达式true

像上面的逻辑与运算符一样,逻辑或运算符使用短路评估来考虑其表达式。如果逻辑或表达式的左侧是true,右侧不计算,因为它不能改变整个表达式的结果。

在下面的例子中,第一个Bool值(hasDoorKey)是false,但第二个值(knowsOverridePassword)是true。因为有一个值true,整体表达式也可以评估true,并且允许访问:

  1. let hasDoorKey = false
  2. let knowsOverridePassword = true
  3. if hasDoorKey || knowsOverridePassword {
  4. print("Welcome!")
  5. } else {
  6. print("ACCESS DENIED")
  7. }
  8. // Prints "Welcome!"

逻辑运算符的组合

您可以组合多个逻辑运算符来创建更长的复合表达式:

  1. if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
  2. print("Welcome!")
  3. } else {
  4. print("ACCESS DENIED")
  5. }
  6. // Prints "Welcome!"

此示例使用多个&&||运算符来创建较长的复合表达式。然而,&&||运算符仍然只能运行两个值,所以这实际上是三个较小的表达式链接在一起。该示例可以解读为:

如果我们输入了正确的门码并通过了视网膜扫描,或者如果我们有一个有效的门钥匙,或者我们知道紧急改写密码,则允许进入。

基于,和的值enteredDoorCode,前两个子表达式是。但是,紧急重写密码是已知的,因此整体复合表达式仍然会评估为。 passedRetinaScan``hasDoorKey``false``true

注意

迅捷逻辑运算符&&||是左结合,这意味着与多个逻辑运算符复合表达式首先评估最左边的子表达式。

显式括号

在不严格需要的情况下包含括号有时很有用,可以使复杂表达式的意图更容易阅读。在上面的门访问示例中,在复合表达式的第一部分周围添加圆括号以使其意图明确是有用的:

  1. if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
  2. print("Welcome!")
  3. } else {
  4. print("ACCESS DENIED")
  5. }
  6. // Prints "Welcome!"

括号清楚地表明前两个值在总体逻辑中被认为是单独的可能状态的一部分。复合表达式的输出不变,但整体意图对读者更清楚。可读性始终优于简洁性; 在他们帮助明确你的意图的地方使用括号。

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

推荐阅读更多精彩内容

  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,115评论 0 13
  • 关于 Swift 重要这个文档所包含的准备信息, 是关于开发的 API 和技术的。这个信息可能会改变, 根据这个文...
    无沣阅读 4,275评论 1 27
  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,731评论 2 9
  • 偶听朋友提到其母和其弟的事情,觉着逗乐,遂记之。 其弟在小学的时候就已颇高颇壮。某日犯错,其母抬手欲训之。其弟公主...
    阴阴阅读 335评论 0 0
  • 刚开始接触小王子的时候,听名字也知道是童话,童话都不是真实的。用一句歌词来说,“童话里都是骗人的”,于是了囫囵...
    笑呵呵的何阅读 446评论 0 0