> IOS开发在线手册 > 表达式(Expressions)

表达式(Expressions)


1.0 翻译:sg552 校对:numbbbbb, stanzhai

2.0 翻译+校对:EudeMorgen

2.1 翻译:mmoaay

2.2 校对:175

3.0 翻译+校对:chenmingjia

Swift 中存在四种表达式:前缀表达式,二元表达式,基本表达式和后缀表达式。表达式在返回一个值的同时还可以引发副作用。

通过前缀表达式和二元表达式可以对简单表达式使用各种运算符。基本表达式从概念上讲是最简单的一种表达式,它是一种访问值的方式。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。

表达式语法
try运算符可选前缀表达式 二元表达式列表可选
表达式 | 表达式 , 表达式列表

Swift Standard Library Operators Reference

除了标准库运算符,你也可以对某个变量使用 & 运算符,从而将其传递给函数的输入输出参数。更多信息,请参阅 输入输出参数。

前缀表达式语法
后缀表达式
前缀表达式输入输出表达式
Swift Standard Library Operators Reference

注意
在解析时,一个二元表达式将作为一个扁平列表表示,然后根据运算符的优先级,再进一步进行组合。例如,2 + 3 * 5 首先被看作具有五个元素的列表,即 2+3*5,随后根据运算符优先级组合为 (2 + (3 * 5))

二元表达式语法
二元表达式二元运算符 前缀表达式
二元表达式赋值运算符 try运算符可选前缀表达式
二元表达式条件运算符 try运算符可选前缀表达式
二元表达式类型转换运算符
二元表达式 二元表达式列表可选

try运算符可选表达式 :

Using Swift with Cocoa and Objective-C (Swift2.2) 中的 Working with Cocoa Data Types。

as? 运算符有条件地执行类型转换,返回目标类型的可选值。在运行时,如果转换成功,返回的可选值将包含转换后的值,否则返回 nil。如果在编译时就能确定转换一定会成功或是失败,则会导致编译报错。

as! 运算符执行强制类型转换,返回目标类型的非可选值。如果转换失败,则会导致运行时错误。表达式 x as! T 效果等同于 (x as? T)!

关于类型转换的更多内容和例子,请参阅 类型转换。

字面量表达式
基本表达式self表达式
基本表达式超类表达式
基本表达式闭包表达式
基本表达式圆括号表达式
基本表达式隐式成员表达式
基本表达式通配符表达式
基本表达式选择器表达式

数组字面量 | 字典字面量
字面量表达式#file | #line | #column | #function

数组字面量项列表可选]
数组字面量项 ,可选 | 数组字面量项 , 数组字面量项列表
数组字面量项表达式

字典字面量项列表 ] | [ : ]
字典字面量项 ,可选 | 字典字面量项 , 字典字面量项列表
字典字面量项表达式 : 表达式

self 方法表达式self 下标表达式 | self 构造器表达式

</a> self 方法表达式self . 标识符
self 下标表达式self [ 表达式 ] 超类方法表达式 | 超类下标表达式 | 超类构造器表达式
超类方法表达式super . 标识符
</a> 超类下标表达式super [ 表达式 ]
函数调用表达式。

捕获列表

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

捕获列表在参数列表之前,由中括号括起来,里面是由逗号分隔的一系列表达式。一旦使用了捕获列表,就必须使用 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 { [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) } 

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

闭包表达式语法

捕获列表 in

捕获列表项列表 ]
捕获列表项 | 捕获列表项 , 捕获列表项列表 捕获说明符可选表达式 表达式元素列表可选) 表达式元素 | 表达式元素 , 表达式元素列表
表达式元素标识符 : 表达式

Using Swift with Cocoa and Objective-C (Swift 3) 中Objective-C Selectors部分。

选择器表达式语法
选择器表达式#selector ( 表达式 )
选择器表达式#selector ( getter:表达式 )
选择器表达式#selector ( setter:表达式 )

Swift Standard Library Operators Reference

后缀表达式语法
函数调用表达式
后缀表达式构造器表达式
后缀表达式显式成员表达式
后缀表达式后缀 self 表达式
后缀表达式dynamicType 表达式
后缀表达式下标表达式
后缀表达式强制取值表达式
后缀表达式可选链表达式

后缀表达式 圆括号表达式
函数调用表达式后缀表达式 圆括号表达式可选尾随闭包
尾随闭包闭包表达式

构造器表达式

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

表达式.init(构造器参数)

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

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

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

// 类型注解是必须的,因为 String 类型有多种构造器
let initializer: Int -> String = String.init
let oneTwoThree = [1, 2, 3].map(initializer).reduce("", combine: +)
print(oneTwoThree)
// 打印 “123” 

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

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

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

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

显式成员表达式

显式成员表达式允许我们访问命名类型、元组或者模块的成员,其形式如下:

表达式.成员名

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

class SomeClass {
    var someProperty = 42
}
let c = SomeClass()
let y = c.someProperty // 访问成员 

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

var t = (10, 20, 30)
t.0 = t.1
// 现在元组 t 为 (20, 20, 30) 

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

为了区分只有参数名有所不同的方法或构造器,在圆括号中写出参数名,参数名后紧跟一个冒号,对于没有参数名的参数,使用下划线代替参数名。而对于重载方法,则需使用类型标注进行区分。例如:

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              // 有歧义
let b = instance.someMethod(_:y:)        // 无歧义

let d = instance.overloadedMethod        // 有歧义
let d = instance.overloadedMethod(_:y:)  // 有歧义
let d: (Int, Bool) -> Void  = instance.overloadedMethod(_:y:)  // 无歧义 

如果点号(.)出现在行首,它会被视为显式成员表达式的一部分,而不是隐式成员表达式的一部分。例如如下代码所展示的被分为多行的链式方法调用:

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

显式成员表达式语法
显式成员表达式十进制数字
显式成员表达式标识符 泛型实参子句可选
显式成员表达式标识符 ( 参数名称 )

参数名 参数名称可选
参数名标识符 :

后缀 self 表达式

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

表达式.self
类型.self

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

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

后缀 self 表达式语法
后缀 self 表达式后缀表达式 . self

函数调用表达式(Function Call Expression)的特殊语法表达式组成,形式如下:

type(of:表达式)

上述形式中的表达式不能是类型名。type(of:) 表达式会返回某个实例在运行时的类型,具体请看下面的例子:

class SomeBaseClass {
    class func printClassName() {
        print("SomeBaseClass")
    }
}
class SomeSubClass: SomeBaseClass {
    override class func printClassName() {
        print("SomeSubClass")
    }
}
let someInstance: SomeBaseClass = SomeSubClass()
// someInstance 在编译时的静态类型为 SomeBaseClass,
// 在运行时的动态类型为 SomeSubClass
type(of: someInstance).printClassName()
// 打印 “SomeSubClass” 

动态类型表达式语法
动态类型表达式 → type(of:表达式) . dynamicType

下标表达式

可通过下标表达式访问相应的下标,形式如下:

表达式[索引表达式]

要获取下标表达式的值,可将索引表达式作为下标表达式的参数来调用下标 getter。下标 setter 的调用方式与之一样。

关于下标的声明,请参阅 协议下标声明。

下标表达式语法
下标表达式后缀表达式 [ 表达式列表 ]

强制取值表达式

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

表达式!

如果该表达式的值不是 nil,则返回解包后的值。否则,抛出运行时错误。

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

var x: Int? = 0
x!++
// x 现在是 1

var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]
someDictionary["a"]![0] = 100
// someDictionary 现在是 [b: [10, 20], a: [100, 2, 3]] 

强制取值语法
强制取值表达式后缀表达式 !

可选链表达式

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

表达式?

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

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

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

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

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

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

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

func someFunctionWithSideEffects() -> Int {
    // 译者注:为了能看出此函数是否被执行,加上了一句打印
    print("someFunctionWithSideEffects") 
    return 42 
}
var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]

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

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

可选链表达式语法
可选链表达式后缀表达式 ?