表达式

未匹配的标注

在 Swift 中,有4种表达式:前缀表达式,二进制表达式,主表达式和后缀表达式。评估表达式返回值,会导致副作用,或两者兼而有之。

前缀表达式和二进制表达式适用于运算符较小的表达式。主表达式在概念上是最简单的表达式,他们提供了一种访问值的方法。后缀表达式,像前缀表达式和二进制表达式一样,让您使用像函数回调和成员访问等更复杂的后缀表达式。每一种表达式在下面的章节中都做了详细的描述。

表达式语法

表达式  → try-operatoropt prefix-expression binary-expressions opt

表达式列表  → expression | expression , expression-list


前缀表达式

前缀表达式 将可选的前缀运算符与表达式组合在一起。前缀运算符接收一个参数,即后面的表达式。

有关运算符行为的信息, 请参阅 基本运算符 和 高级运算符

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

除了标准库运算符之外,还可以使用 & 紧接在作为函数调用的输入输出参数变量名称之前。有关更多信息和示例,请参阅 In-Out 参数

前缀表达式的语法

前缀表达式  → prefix-operator opt postfix-expression

前缀表达式  → in-out-expression

in-out 表达式  → & identifier

Try 运算符

try 表达式 是由 try 运算符后跟一个可以抛出错误的表达式构成。它具有以下形式:

try expression

可选 try 表达式 是由 try? 运算符后跟一个可以抛出错误的表达式构成。它具有以下形式:

try? expression

如果这个 表达式 没有抛出错误,则此表达式的值为可选的,包含了 表达式 的值。否则,该可选 try 表达式的值为 nil

强制 try 表达式 是由 try! 运算符后跟一个可以抛出错误的表达式构成。它具有以下形式:

try! expression

如果该 表达式 抛出一个错误,则会产生一个运行时错误。

当二元运算符左侧的表达式用 trytry?try! 标记时,则该运算符作用于整个二元表达式。换句话说,你可以用括号来显式指定运算符的作用域。

sum = try someThrowingFunction() + anotherThrowingFunction()   // try 同时作用于两个函数调用
sum = try (someThrowingFunction() + anotherThrowingFunction()) // try 同时作用于两个函数调用
sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误: try 仅作用于第一个函数调用

try 表达式不能出现在二元运算符右侧,除非二元运算符是赋值运算符或者 try 表达式是在括号之内。

有关更多信息和查看如何使用 trytry?,和 try! 的示例,请参阅 错误处理

try 表达式的语法

try 操作符  → try |try ? | try !


二元表达式

二元表示式 是由带有表达式的中缀二元操作符组成,其中表达式接收左操作数和右操作数为参数。形式如下:

左操作数参数 操作符 右操作数参数

关于这些操作符行为的信息, 请查看 基本操作符 和 高级操作符.

关于由Swift标准库提供的操作符的信息,请查看 操作符声明.

注意

在解析时,由二元操作符组成的表达式被表示为扁平列表(flat list)。通过采用操作符优先算法把列表转化成一个树结构。例如,表达式 2 + 3 * 5 在初始阶段被理解为拥有五个元素的扁平列表, 2, +, 3, *,和 5。解析流程会把它们转化成树结构 (2 + (3 * 5))。

二元表达式的语法

二元表达式  → 二元操作符 前缀表达式

二元表达式  → 赋值操作符 try 操作符 opt 前缀表达式

二元表达式  → 条件操作符 try 操作符 opt 前缀表达式

二元表达式  → 类型转换表达式

二元表达式 → 二元表达式 二元表达式 opt

赋值操作符

赋值操作符 给指定的表达式设置一个新值。形式如下:

表达式 = 值

表达式 的值通过计算 获得。如果 表达式 是一个元组,那么 必须是具有相同元素个数的元组。(允许复合元组。)用 的各个部分给相应的 表达式 的各个部分赋值。例如:

(a, _, (b, c)) = ("test", 9.45, (12, 3))
// a 是 "test", b 是 12, c 是 3, 而 9.45 被忽略。

赋值操作符不返回任何值。

赋值操作符的语法

赋值操作符  → =

三元条件操作符

三元条件操作符 是基于一个条件的值来计算两个给定的值中其中之一。形式如下:

条件 ? 表达式1 : 表达式2

如果 条件 计算为 ,条件操作符将会计算第一个表达式的值,并返回该值。反之,则计算第二个表达式的值,并返回该值。没有被使用的表达式则不会被计算。

对于使用三元条件操作符的例子,请查看 三元条件操作符.

条件操作符的语法

条件操作符 → ? 表达式 :

类型转换操作符

有四种类型转换操作符:isasas?as!

它们有如下形式:

表达式 is 类型
表达式 as 类型
表达式 as? 类型
表达式 as! 类型

is 操作符在运行时检查 表达式 是否可以转换为指定的 类型 ,假如可以,则返回 ,否则,返回

as 操作符是在编译期执行类型转换,众所周知,转换也一直是成功的,例如向上转型和桥接。向上转型可以在表达式中使用类型的父类型的实例,而不需要使用中间变量。下面的方法是等价的:

func f(_ any: Any) { print("Function for Any") }
func f(_ int: Int) { print("Function for Int") }
let x = 10
f(x)
// 打印 "Function for Int"

let y: Any = x
f(y)
// 打印 "Function for Any"

f(x as Any)
// 打印 "Function for Any"

桥接可以使用Swift标准库中的类型来代替Foundation中的类型,例如  String 可以作为 NSString 使用,并且不需要创建一个新的实例。有关桥接的更多信息,请查看 配合使用 Foundation 类型

as? 操作符把 表达式 按照一定的条件转换成指定的 类型, 并返回指定 类型 的可选类型。在运行时期,如果转型成功,表达式 的值被包装成可选类型,并返回。否则,返回 nil。如果指明确保转换成指定 类型 成功或者失败,那么在编译期就会报错。

as! 操作符则把 表达式 强制转换成指定 类型,并返回一个指定 类型 的值,而不是可选类型。如果转型失败,就会抛出运行时错误。x as! T 与 (x as? T)! 是等效的。

更多关于类型转换以及使用类型转换操作符的例子,请查看 类型转换.

类型转换操作符语法

类型转换操作符  → is 类型

类型转换操作符  → as 类型

类型转换操作符  → as ? 类型

类型转换操作符  → as ! 类型


主表达式

主表达式 是最基本的表达式。它们可以单独使用,也可以和前缀表达式,二元表达式,后缀表达式组合使用。

主表达式语法

主表达式  → 标识符 泛型参数子句 可选

主表达式  → 字符型表达式

主表达式  → self 表达式

主表达式  → 超类表达式

主表达式  → 闭包表达式

主表达式  → 圆括号表达式

主表达式  → 元组表达式

主表达式  → 隐式成员表达式

主表达式  → 通配符表达式

主表达式  → 关键路径表达式

主表达式  → 选择器表达式

主表达式  → 字符串关键路径表达式

字符型表达式

字符型表达式 由普通字符(如字符串或数字)、数组或字典字符、Playground 字符或以下特殊字符之一组成:

字符(Literal) 类型(Type) 值(Value)
#file String 所在的文件名称。
#line Int 所在的行号。
#column Int 所在的列号。
#function String 所在 function 的名称。

在函数内部,#function 的输出值是该函数的名称,在方法中它是该方法的名称,在 getter 或 setter 属性中它是该属性的名称, 在像 init 或 subscript 之类的特殊成员中它是该关键字的名称,在文件的顶层它是当前模块的名称。

当用作函数或方法参数的默认值时,在调用的地方计算默认值表达式时,将确定特殊字符的值。

func logFunctionName(string: String = #function) {
    print(string)
}
func myFunction() {
    logFunctionName() // 打印结果: "myFunction()".
}

数组字符 是一个有序的值的集合。 它具有以下形式:

[value 1, value 2, ...]

Self 表达式

self 表达式是当前类型或者类型内部使用的实例的显式的引用。形式如下:

self
self.成员 名字
self[下标 索引]
self(初始化器 参数)
self.init(初始化器 参数)

在初始化器,下标,或者实例方法中,self 指的是当前类型的实例,而在类型方法中,指的是当前类型。

当访问成员变量时,self 表达式被用来指定作用域。比如对于一个函数的形参,当在作用域中,还有另一个相同名称的变量时,可以用 self 来消除歧义。例如:

class SomeClass {
    var greeting: String
    init(greeting: String) {
        self.greeting = greeting
    }
}

在一个值类型的可变方法中,可以给 self 分配一个该值类型的新的实例。例如:

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        self = Point(x: x + deltaX, y: y + deltaY)
    }
}

self 表达式的语法

self 表达式  → self | self 方法表达式 | self 下标表达式 | self 初始化器表达式

self 方法表达式  → self . 标识符

self 下标表达式  → self [ 函数调用参数列表 ]

self 初始化器  → self . init

超类表达式

超类表达式 可以使我们在某个class中与它的超类进行交互。它具有以下形式:

super.member name
super[subscript index]
super.init(initializer arguments)

第一种形式用于访问超类的某个成员(member)。第二种形式用于访问该超类的下标(subscript)实现。第三种形式用于访问超类的构造器(initializer)。

子类(subclass)可以通过超类(superclass)表达式在它们的 member, subscripting 和 initializers 中来利用它们超类中的某些实现(既有的方法或者逻辑)。

超类(superclass)表达式语法

超类表达式  → 超类方法表达式 | 超类下标表达式 | 超类构造器表达式

超类方法表达式  → super . 标识符

超类下标表达式  → super [ 方法调用参数列表 ]

超类构造器表达式  → super . init

闭包表达式

闭包表达式 用于创建一个闭包,在其他编程语言中也被称为拉姆达匿名函数。与函数声明一样,闭包包含语句,它从其封闭范围捕获常量和变量。它具有以下形式:

{ (parameters) -> return type in
    statements
}

闭包的 参数 和一个函数声明的参数具有相同的格式,如在 函数声明中描述的一样。

有几种特殊形式可以更简洁地编写闭包:

  • 闭包可以省略其参数的类型,返回类型或两者。如果省略参数名称和两种类型,在语句之前可以省略 in 关键字。 如果无法推断省略的类型,则会引发编译时错误。
  • 闭包可以省略其参数的名称。然后将其参数隐式命名 $ ,接着他们的位置:$0$1$2, 以此类推.
  • 仅包含单个表达式的闭包被理解为返回该表达式的值。在对周围表达式执行类型推断时,还会考虑此表达式的内容。

以下闭包表达式是等效的:

myFunction { (x: Int, y: Int) -> Int in
    return x + y
}

myFunction { x, y in
    return x + y
}

myFunction { return $0 + $1 }

myFunction { $0 + $1 }

有关将闭包作为参数传递给函数的信息,请参阅 函数调用表达式.

闭包表达式可以在不存储在变量或常量中的情况下使用,例如当您立即使用闭包作为函数调用的一部分时。传递给myFunction上面代码的闭包表达式就是这种直接使用的例子。因此,闭包表达式是转义还是非转义取决于表达式的周围上下文。如果立即调用闭包表达式或作为非转义函数参数传递,则闭包表达式是非转义表达式。否则,闭包表达式是转义表达式。

有关转义闭包的详细信息,请参阅 转义闭包

捕获列表

默认情况下,闭包会捕获附近作用域中的常量和变量,并使用强引用指向它们。你可以通过一个捕获列表来显式指定它的捕获行为。

捕获列表在参数列表之前,由中括号括起来,里面是由逗号分隔的一系列表达式。一旦使用了捕获列表,就必须使用 in 关键字,即使省略了参数名、参数类型和返回类型。

捕获列表中的项会在闭包创建时被初始化。每一项都会用闭包附近作用域中的同名常量或者变量的值初始化。例如下面的代码示例中,捕获列表包含 a 而不包含 b ,这将导致这两个变量具有不同的行为。

var a = 0
var b = 0
let closure = { [a] in
    print(a, b)
}

a = 10
b = 10
closure()
// 打印 "0 10"

在示例中,变量 b 只有一个,然而,变量 a 有两个,一个在闭包外,一个在闭包内。闭包内的变量 a 会在闭包创建时用闭包外的变量  a 的值来初始化,除此之外它们并无其他联系。这意味着在闭包创建后,改变某个 a 的值都不会对另一个 a 的值造成任何影响。与此相反,闭包内外都是同一个变量 b ,因此在闭包外改变其值,闭包内的值也会受影响。

如果闭包捕获的值具有引用语义则有所不同。例如,下面示例中,有两个变量 x,一个在闭包外,一个在闭包内,由于它们的值是引用语义,虽然这是两个不同的变量,它们却都引用着同一实例。

class SimpleClass {
    var value: Int = 0
}
var x = SimpleClass()
var y = SimpleClass()
let closure = { [x] in
    print(x.value, y.value)
}

x.value = 10
y.value = 10
closure()
// 打印 "10 10"

如果捕获列表中的值是类类型,你可以使用 weak 或者 unowned 来修饰它,闭包会分别用弱引用和无主引用来捕获该值。

myFunction { print(self.title) }                    // 隐式强引用捕获
myFunction { [self] in print(self.title) }          // 显式强引用捕获
myFunction { [weak self] in print(self!.title) }    // 弱引用捕获
myFunction { [unowned self] in print(self.title) }  // 无主引用捕获

在捕获列表中,也可以将任意表达式的值绑定到一个常量上。该表达式会在闭包被创建时进行求值,闭包会按照指定的引用类型来捕获表达式的值。例如:

// 以弱引用捕获 self.parent 并赋值给 parent
myFunction { [weak parent = self.parent] in print(parent!.title) }

关于闭包表达式的更多信息和例子,请参阅 闭包表达式. 关于捕获列表的更多信息和例子,请参阅 解决闭包引起的循环强引用.

闭包表达式语法

闭包表达式 → { 闭包签名 opt 语句 opt }

闭包签名  → 捕获列表 opt 捕获参数子句 throws opt函数结果 opt in

闭包签名  → 捕获列表 in

捕获参数子句  → ( ) | ( 捕获参数列表 ) | 标识符列表

捕获参数列表  → 捕获参数 | 捕获参数 , 捕获参数列表

捕获参数 → 捕获参数名称 类型标注 opt

捕获参数 → 捕获参数名称 类型标注 ...

捕获参数名称  → 标识符

捕获列表  → [捕获列表项 ]

捕获列表项 → 捕获列表项 | 捕获列表项 , 捕获列表项

捕获列表项  → 捕获说明符 opt 表达式

捕获说明符  → weak | unowned | unowned(safe) | unowned(unsafe)

圆括号表达式

圆括号表达式是由圆括号包围的表达式。你可以用圆括号说明成组的表达式的先后操作。成组的圆括号不会改变表达式的类型 - 例如 (1) 的类型就是简单的 Int

圆括号表达式语法

圆括号表达式  → ( 表达式 )

元组表达式

元组表达式由圆括号和其中多个逗号分隔的子表达式组成。每个子表达式前面可以有一个标识符,用冒号 (:) 隔开。元组表达式形式如下:

(identifier 1: expression 1, identifier 2: expression 2, ...)

元组表达式可以一个表达式都没有,也可以包含两个或是更多的表达式。单个表达式用括号括起来就是括号表达式了。

注意

在 Swift 中,空元组表达式和空元组类型都被写为 () 。因为 Void 是一个类型别名,用于 () ,所以可以使用它来编写空元组类型。但是,就像所有类型别名一样,Void  总是一种类型,不能用它来写一个空元组表达式。

元组表达式语法

元组表达式  → ( ) | ( 元组元素 , 元组元素列表 )

元组元素列表 → 元组元素 | 元组元素 , tuple-element-list

元组元素  → 表达式 | 标识符 : 表达式

通配符表达式

通配符表达式可以在赋值过程中显式忽略某个值。例如下面的代码中,10 被赋值给 x ,而 20 则被忽略:

(x, _) = (10, 20)
// x 为 10,20 被忽略

通配符表达式语法

通配符表达式  → _

Key-Path 表达式

key-path 表达式  是一种类型的属性或下标。在动态编程任务中使用key-path 表达式,例如键值观察。它们具有以下形式:

\type name.path

类型名 是具体类型的名称,包括任何泛型参数,例如 String[Int], 或 Set<Int>

 path  由属性名称、下标、可选链式表达式和强制展开表达式组成。这些关键路径组件中的每一个可以按需要按任意顺序重复多次。

在编译时,key-path 表达式由 KeyPath 取代.

若要使用密钥路径访问值,请将密钥路径传递到 subscript(keyPath:)下标,这在所有类型上都可用。例如:

struct SomeStructure {
    var someValue: Int
}

let s = SomeStructure(someValue: 12)
let pathToProperty = \SomeStructure.someValue

let value = s[keyPath: pathToProperty]
// 值是 12

在类型推断可以确定隐含类型的上下文中,省略了类型名称
。下面的代码使用了 \.someProperty ,而不是\SomeClass.someProperty

class SomeClass: NSObject {
    @objc var someProperty: Int
    init(someProperty: Int) {
        self.someProperty = someProperty
    }
}

let c = SomeClass(someProperty: 10)
c.observe(\.someProperty) { object, change in
    // ...
}

路径 可以包含多个属性名,由句点分隔,以引用属性值的属性。此代码使用 key path 表达式 \OuterStructure.outer.someValue 获取 OuterStructure  类型的 outer 属性的 someValue 性质。

struct OuterStructure {
    var outer: SomeStructure
    init(someValue: Int) {
        self.outer = SomeStructure(someValue: someValue)
    }
}

let nested = OuterStructure(someValue: 24)
let nestedKeyPath = \OuterStructure.outer.someValue

let nestedValue = nested[keyPath: nestedKeyPath]
// nestedValue is 24

只要下标的参数类型符合 Hashable 协议,path 可以包括使用括号的下标。此示例使用关键路径中的下标来访问数组的第二个元素:

let greetings = ["hello", "hola", "bonjour", "안녕"]
let myGreeting = greetings[keyPath: \[String].[1]]
// myGreeting is 'hola'

下标中使用的值可以是命名值或文字值。使用值语义在关键路径中捕获值。下面的代码在 key path 表达式和闭包中使用变量 index  来访问 greetings 数组的第三个元素。当 index 被修改时,关键路径表达式仍然引用第三个元素,而闭包使用新索引。

var index = 2
let path = \[String].[index]
let fn: ([String]) -> String = { strings in strings[index] }

print(greetings[keyPath: path])
// Prints "bonjour"
print(fn(greetings))
// 打印 "bonjour"

// Setting 'index' to a new value doesn't affect 'path'
index += 1
print(greetings[keyPath: path])
// 打印 "bonjour"

// Because 'fn' closes over 'index', it uses the new value
print(fn(greetings))
// 打印 "안녕"

路径 可以使用可选链接和强制展开。此代码使用关键路径中的可选链接来访问可选字符串的属性:

let firstGreeting: String? = greetings.first
print(firstGreeting?.count as Any)
// 打印 "Optional(5)"

// Do the same thing using a key path.
let count = greetings[keyPath: \[String].first?.count]
print(count as Any)
// 打印 "Optional(5)"

可以混合和匹配关键路径的组件,以访问类型中嵌套的值。下面的代码通过使用组合这些组件的键路径表达式来访问数组字典的不同值和属性。

let interestingNumbers = ["prime": [2, 3, 5, 7, 11, 13, 15],
                          "triangular": [1, 3, 6, 10, 15, 21, 28],
                          "hexagonal": [1, 6, 15, 28, 45, 66, 91]]
print(interestingNumbers[keyPath: \[String: [Int]].["prime"]] as Any)
// 打印 "Optional([2, 3, 5, 7, 11, 13, 15])"
print(interestingNumbers[keyPath: \[String: [Int]].["prime"]![0]])
// 打印 "2"
print(interestingNumbers[keyPath: \[String: [Int]].["hexagonal"]!.count])
// 打印 "7"
print(interestingNumbers[keyPath: \[String: [Int]].["hexagonal"]!.count.bitWidth])
// 打印 "64"

有关在与 Objective-C API 交互的代码中使用关键路径的更多信息,请参阅 在Swift 中使用Objective-C 运行时特性。 有关键值编码和键值观察的信息,请参阅 键值编码编程指南 和 关键值观察程序指南

key-path 表达式语法

key-path 表达式  → \ 类型 opt .  key-path 组件

key-path 组件  → key-path 组件 | key-path 组件 . key-path 组件

key-path 组件  → 标识符 key-path 后缀 opt | key-path 后缀

key-path 后缀 → key-path 后缀 key-path 后缀 opt

key-path 后缀  → ? | ! | [ 函数调用参数列表 ]

选择器表达式

选择器表达式允许您访问用于引用 Objective-C 中的方法或属性的 getter 或 setter 的选择器。

#selector(method name)
#selector(getter: property name)
#selector(setter: property name)

方法名和属性名必须是存在于 Objective-C 运行时中的方法和属性的引用。选择器表达式的返回值是一个 Selector 类型的实例。例如:

class SomeClass: NSObject {
    @objc let property: String
    @objc(doSomethingWithInt:)
    func doSomething(_ x: Int) {}

    init(property: String) {
        self.property = property
    }
}
let selectorForMethod = #selector(SomeClass.doSomething(_:))
let selectorForPropertyGetter = #selector(getter: SomeClass.property)

当为属性的 getter 创建选择器时,属性名可以是变量属性或者常量属性的引用。但是当为属性的 setter 创建选择器时,属性名只可以是对变量属性的引用。

方法名称可以包含圆括号来进行分组,并使用 as操作符来区分具有相同方法名但类型不同的方法,例如:

extension SomeClass {
    @objc(doSomethingWithString:)
    func doSomething(_ x: String) { }
}
let anotherSelector = #selector(SomeClass.doSomething(_:) as (SomeClass) -> (String) -> Void)

由于选择器是在编译时创建的,因此编译器可以检查方法或者属性是否存在,以及是否在运行时暴露给了 Objective-C 。

注意

虽然方法名或者属性名是个表达式,但是它不会被求值。

更多关于如何在 Swift 代码中使用选择器来与 Objective-C API 进行交互的信息,请参阅 Using Objective-C Runtime Features in Swif....

选择器表达式语法

选择器表达式  → #selector ( 表达式 )

选择器表达式  → #selector ( getter: 表达式 )

选择器表达式  → #selector ( setter: 表达式 )

Key-Path 字符串表达式

key-path 字符串表达式允许您访问用于引用 Objective-C 中的属性的字符串,用于键值编码和键值观察API。它有以下形式:

#keyPath(property name)

属性名 必须是 Objtovi-C 运行时可用的属性的引用。在编译时,key-path 字符串表达式由字符串文字替换。例如:

class SomeClass: NSObject {
    @objc var someProperty: Int
    init(someProperty: Int) {
        self.someProperty = someProperty
    }
}

let c = SomeClass(someProperty: 12)
let keyPath = #keyPath(SomeClass.someProperty)

if let value = c.value(forKey: keyPath) {
    print(value)
}
// 打印 "12"

当在类中使用 key-path 字符串表达式时,可以通过只写入属性名而不写入类名来引用该类的属性。

extension SomeClass {
    func getSomeKeyPath() -> String {
        return #keyPath(someProperty)
    }
}
print(keyPath == c.getSomeKeyPath())
// 打印 "true"

因为关 key path 字符串是在编译时创建的,而不是在运行时创建的,编译器可以检查该属性是否存在,并将该属性暴露给 Objtovi-C 运行时。

有关与 Objective-C API 交互的 Swift 代码中使用关 key path 的更多信息,请参阅 Using Objective-C Runtime Features in Swif....。有关键值编码和键值观察的信息,请参阅 键值编码编程指南 和 关键值观察程序指南.

注意

虽然 property name  是表达式,但它从不被评估。

key-path 字符串表达式

key-path 字符串表达式  → #keyPath ( 表达式 )


后缀表达式

后缀表达式就是在某个表达式的后面运用后缀运算符或其他后缀语法。从语法构成上来看,基本表达式也是后缀表达式。

关于这些运算符的更多信息,请参阅 基本运算符 和 高级运算符.

关于 Swift 标准库提供的运算符的更多信息,请参阅 运算符声明.

后缀表达式语法

后缀表达式 → 基本表达式

后缀表达式 → 后缀表达式 后缀运算符

后缀表达式 → 函数调用表达式

后缀表达式 → 构造器表达式

后缀表达式 → 显式成员表达式

后缀表达式 → 后缀 self 表达式

后缀表达式 → 下标表达式

后缀表达式 → 强制取值表达式

后缀表达式 → 可选链表达式

函数调用表达式

函数调用表达式 由函数名和括号中用逗号分隔的函数参数列表组成。函数调用表达式具有以下形式:

function name(argument value 1, argument value 2)

函数调用表达式由函数名和参数列表组成,形式如下:

如果函数定义包括其参数的名称,则函数调用必须在其参数值之前包括名称,参数值由冒号 (:) 分隔。这种函数调用表达式有以下形式:

function name(argument name 1: argument value 1, argument name 2: argument value 2)

函数调用表达式可以包括紧接在关闭括号之后的闭包表达式形式的尾随闭包。尾随闭包被理解为函数的参数,在最后括号参数之后添加。以下函数调用是等价的:

// someFunction takes an integer and a closure as its arguments
someFunction(x: x, f: {$0 == 13})
someFunction(x: x) {$0 == 13}

如果尾随闭包是函数唯一的参数,则可以省略括号。

// someMethod takes a closure as its only argument
myData.someMethod() {$0 == 13}
myData.someMethod {$0 == 13}

函数调用表达式语法

函数调用表达式  → 后缀表达式 函数调用参数子句

函数调用表达式  → 后缀表达式 函数调用参数子句opt 尾迹闭合

函数调用参数子句  → ( ) | ( 函数调用参数列表 )

function-call-argument-list  → 函数调用参数 | 函数调用参数 ,函数调用参数列表

函数调用参数子句  → 表达式 | 标识符 : expression

函数调用参数  → 运算符 | 标识符 : 运算符

尾迹闭合  → 闭合表达式

构造器表达式

构造器表达式用于访问某个类型的构造器,形式如下:

expression.init(initializer arguments)

你可以在函数调用表达式中使用构造器表达式来初始化某个类型的新实例。也可以使用构造器表达式来代理给超类构造器。

class SomeSubClass: SomeSuperClass {
    override init() {
        // 此处为子类构造过程
        super.init()
    }
}

和函数类似,构造器表达式可以作为一个值。 例如:

// Type annotation is required because String has multiple initializers.
let initializer: (Int) -> String = String.init
let oneTwoThree = [1, 2, 3].map(initializer).reduce("", +)
print(oneTwoThree)
// 打印 "123"

如果通过名字来指定某个类型,可以不用构造器表达式而直接使用类型的构造器。在其他情况下,你必须使用构造器表达式。

let s1 = SomeType.init(data: 3)  // 有效
let s2 = SomeType(data: 1)       // 也有效

let s3 = type(of: someValue).init(data: 7)  // 有效
let s4 = type(of: someValue)(data: 5)       // 错误

构造器表达式语法

构造器表达式  → 后缀表达式 . init

构造器表达式  → 后缀表达式 . init ( 参数名称 )

显式成员表达式

一个显式成员表达式 允许访问命名类型、元组或模块的成员。它由项目和成员的标识符之间的一个周期 (.) 组成。

expression.member name

命名类型的某个成员在原始实现或者扩展中定义,例如:

class SomeClass {
    var someProperty = 42
}
let c = SomeClass()
let y = c.someProperty  // Member access

元组的成员会隐式地根据表示它们出现顺序的整数来命名,以 0 开始,例如:

var t = (10, 20, 30)
t.0 = t.1
// Now t is (20, 20, 30)

对于模块的成员来说,只能直接访问顶级声明中的成员。

用 dynamicMemberLookup 属性声明的类型包括在运行时查找的成员,如 属性

为了区分方法或构造器,它们的名称仅由它们的参数名不同,在括号中包含参数名称,每个参数名后面跟着冒号 (:) 。为一个没有名字的参数写下划线 (_) 。要区分重载方法,请使用类型注释。例如:

class SomeClass {
    func someMethod(x: Int, y: Int) {}
    func someMethod(x: Int, z: Int) {}
    func overloadedMethod(x: Int, y: Int) {}
    func overloadedMethod(x: Int, y: Bool) {}
}
let instance = SomeClass()

let a = instance.someMethod              // Ambiguous
let b = instance.someMethod(x:y:)        // Unambiguous

let d = instance.overloadedMethod        // Ambiguous
let d = instance.overloadedMethod(x:y:)  // Still ambiguous
let d: (Int, Bool) -> Void  = instance.overloadedMethod(x:y:)  // Unambiguous

如果句点出现在行的开头,则将其理解为显式成员表达式的一部分,而不是隐式成员表达式。例如,下面的列表显示链式方法调用在多个行上拆分:

let x = [10, 3, 20, 15, 4]
    .sorted()
    .filter { $0 > 5 }
    .map { $0 * 100 }

显式成员表达式语法

显式成员表达式  → 后缀表达式 . decimal-digits

显式成员表达式  → 后缀表达式 . 标识符 泛型参数子句 opt

显式成员表达式  → 后缀表达式 . 标识符 ( 参数名称 )

参数名称  → 参数名称 参数名称 opt

参数名称  → 标识符 :

后缀 Self 表达式

后缀 self  表达式由某个表达式或类型名紧跟 .self 组成,其形式如下:

expression.self
type.self

第一种形式返回表达式的值。例如:x.self 返回 x

第二种形式返回相应的类型。我们可以用它来获取某个实例的类型作为一个值来使用。例如, SomeClass.self 会返回 SomeClass 类型本身,你可以将其传递给相应函数或者方法作为参数。

self 表达式语法

后缀 self 表达式  → 后缀表达式 . self

下标表达式

可通过 下标表达式 使用相应的下标声明 getter 和 setter 来提供下标访问。它有以下形式:

expression[index expressions]

为了计算下标表达式的值,使用 index expressions  作为下标参数传递,调用下标类型的getter。若要设置其值,则以相同的方式调用下标集。

有关下标声明的信息,请参阅 Protocol Subscript Declaration.

下标表达式语法

下标表达式  → 后缀表达式 [ 函数调用参数列表 ]

强制取值表达式

当你确定可选值不是 nil 时,可以使用强制取值表达式来强制解包,形式如下:

expression!

如果 expression  的值不是 nil ,则可选值展开并返回相应的非可选类型。否则,会引发运行时错误。

返回的值可以被修改,无论是修改值本身,还是修改值的成员。例如:

var x: Int? = 0
x! += 1
// x is now 1

var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]
someDictionary["a"]![0] = 100
// someDictionary is now ["a": [100, 2, 3], "b": [10, 20]]

强制取值表达式语法

强制取值表达式  → 后缀表达式 !

可选链表达式

可选链表达式提供了一种使用可选值的便捷方法,形式如下:

expression?

后缀 ?  运算符会根据表达式生成可选链表达式而不会改变表达式的值。

如果某个后缀表达式包含可选链表达式,那么它的执行过程会比较特殊。如果该可选链表达式的值是 nil,整个后缀表达式会直接返回  nil。如果该可选链表达式的值不是 nil,则返回可选链表达式解包后的值,并将该值用于后缀表达式中剩余的表达式。在这两种情况下,整个后缀表达式的值都会是可选类型。

如果某个后缀表达式中包含了可选链表达式,那么只有最外层的表达式会返回一个可选类型。例如,在下面的例子中,如果 c 不是 nil,那么它的值会被解包,然后通过 .property 访问它的属性,接着进一步通过 .performAction() 调用相应方法。整个 c?.property.performAction()  表达式返回一个可选类型的值,而不是多重可选类型。

var c: SomeClass?
var result: Bool? = c?.property.performAction()

上面的例子跟下面的不使用可选链表达式的例子等价:

var result: Bool?
if let unwrappedC = c {
    result = unwrappedC.property.performAction()
}

可选链表达式解包后的值可以被修改,无论是修改值本身,还是修改值的成员。如果可选链表达式的值为 nil ,则表达式右侧的赋值操作不会被执行。例如:

func someFunctionWithSideEffects() -> Int {
    return 42  // No actual side effects.
}
var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]

someDictionary["not here"]?[0] = someFunctionWithSideEffects()
// // someFunctionWithSideEffects 不会被执行
// someDictionary is still ["a": [1, 2, 3], "b": [10, 20]]

someDictionary["a"]?[0] = someFunctionWithSideEffects()
//  someFunctionWithSideEffects 被执行并返回 42
// someDictionary is now ["a": [42, 2, 3], "b": [10, 20]]

可选链表达式语法

可选链表达式  → 后缀表达式 ?

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/the-swift-progr...

译文地址:https://learnku.com/docs/the-swift-progr...

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~