第6章 表达式

运算符可以算作是一个媒介,是一个命令编译器对一个或多个操作对象执行某种运算的符号。而表达式是由运算符、常量和变量构成的式子。Swift 语言中运算符和表达式数量之多,在高级语言中是少见的。正是这些丰富的运算符和表达式,使得Swift语言的功能变得十分完善,这也是Swift语言的主要特点之一。在本节的内容中,将详细讲解Swift表达式的基本知识。

6.1 前缀表达式

在Swift语言中,存在了如下所示的4种表达式。

□ 前缀(prefix)表达式。

□ 二元(binary)表达式。

□ 主要(primary)表达式。

□ 后缀(postfix)表达式。

在Swift语言中,表达式可以返回一个值,以及运行某些逻辑(causes a side effect)。前缀表达式和二元表达式就是对某些表达式使用各种运算符(operators)。主要表达式是最短小的表达式,它提供了获取(变量的)值的一种途径。后缀表达式则允许你建立复杂的表达式,例如配合函数调用和成员访问。

在Swift语言中,前缀表达式由前缀符号和表达式组成,这个前缀符号只能接收一个参数。Swift标准库支持如下所示的前缀运算符。

□ ++ 自增1(increment)。

□ -- 自减1(decrement)。

□ ! 逻辑否(Logical NOT)。

□ ~ 按位否(Bitwise NOT)。

□ + 加(Unary plus)。

□ - 减(Unary minus)。

对于上述运算符的使用,请读者参阅本书的“运算符”一章内容。

6.2 二元表达式

在Swift语言中,二元表达式由“左边参数”+“二元运算符”+“右边参数”组成,其具体语法格式如下所示。

    left-hand argument operator right-hand argument

在Swift 标准库中提供了如下所示的二元运算符。

(1)求幂相关,无结合,优先级160。

□ << 按位左移(Bitwise left shift)。

□ 按位右移(Bitwise right shift)。

(2)乘除法相关,左结合,优先级150。

□ * 乘。

□ / 除。

□ % 求余。

□ &* 乘法,忽略溢出(Multiply, ignoring overflow)。

□ &/ 除法,忽略溢出(Divide, ignoring overflow)。

□ &% 求余,忽略溢出(Remainder, ignoring overflow)。

□ & 位与(Bitwise AND)。

(3)加减法相关,左结合,优先级140。

□ + 加。

□ - 减。

□ &+ Add with overflow。

□ &- Subtract with overflow。

□ | 按位或(Bitwise OR)。

□ ^ 按位异或(Bitwise XOR)。

(4)Range,无结合,优先级135。

□ .. 半闭值域 Half-closed range。

□ ... 全闭值域 Closed range。

(5)类型转换,无结合,优先级132。

□ is 类型检查(type check)。

□ as 类型转换(type cast)。

(6)Comparative,无结合,优先级130。

□ < 小于。

□ <= 小于等于。

□ 大于。

□ = 大于等于。

□ == 等于。

□ != 不等。

□ === 恒等于。

□ !== 不恒等。

□ ~= 模式匹配(Pattern match)。

(7)合取(Conjunctive),左结合,优先级120。

□ && 逻辑与(Logical AND)。

(8)析取(Disjunctive)左结合,优先级110。

□ || 逻辑或(Logical OR)。

(9)三元条件(Ternary Conditional),右结合,优先级100。

□ ?: 三元条件 Ternary conditional。

(10)赋值(Assignment)(右结合,优先级90)。

= 赋值(Assign)。

□ *= Multiply and assign。

□ /= Divide and assign。

□ %= Remainder and assign。

□ += Add and assign。

□ -= Subtract and assign。

□ <<= Left bit shift and assign。

□ = Right bit shift and assign。

□ &= Bitwise AND and assign。

□ ^= Bitwise XOR and assign。

□ |= Bitwise OR and assign。

□ &&= Logical AND and assign。

□ ||= Logical OR and assign。

注意

在解析上述表达式时,一个二元表达式表示为一个一级数组(a flat list),这个数组(List)根据运算符的先后顺序,被转换成了一个tree。例如,2 + 3 5首先被认为是:2, + , 3, , 5,随后它被转换成tree(2 +(3 * 5))。

6.3 赋值表达式

在Swift语言中,赋值表达式会对某个给定的表达式赋值,其具体形式如下所示。

    expression = value

上述语法格式的功能是,把右边的 value 赋值给左边的 expression,如果左边的expression 需要接收多个参数(是一个tuple ),那么右边必须也是一个具有同样数量参数的tuple(允许嵌套的tuple)。例如,如下所示的演示代码。

    (a, _, (b, c)) = ("test", 9.45, (12, 3))
    // a is "test", b is 12, c is 3, and 9.45 is ignored

赋值运算符不返回任何值。

6.4 三元条件运算符

在Swift语言中,三元条件运算符的功能是根据条件来获取值,其具体形式如下所示。

    condition ? expression used if true : expression used if false

在上述格式中,如果 condition 是true,那么返回第一个表达式的值(此时不会调用第二个表达式),否则返回第二个表达式的值(此时不会调用第一个表达式)。

6.5 类型转换运算符

在Swift语言中有两种类型转换操作符,分别是as和is,具有如下所示的表现形式。

    expression as type
    expression as? type
    expression is type

在Swift语言中,as 运算符会把目标表达式转换成指定的类型(specified type),具体过程如下所示。

(1)如果类型转换成功,那么目标表达式就会返回指定类型的实例(instance)。例如,当把子类(subclass)变成父类(superclass)时就是如此。

(2)如果转换失败,则会抛出编译错误(compile-time error)。

如果上述两个情况都不是,即编译器在编译时期无法确定转换能否成功,那么目标表达式就会变成指定的类型optional。然后在运行时,如果转换成功,目标表达式就会作为 optional的一部分来返回。否则,目标表达式返回nil。与之对应的例子是把一个superclass 转换成一个 subclass。例如,如下所示的演示代码。

    class SomeSuperType {}
    class SomeType: SomeSuperType {}
    class SomeChildType: SomeType {}
    let s = SomeType()
    let x = s as SomeSuperType    // known to succeed; type is SomeSuperType
    let y = s as Int          // known to fail; compile-time error
    let z = s as SomeChildType    // might fail at runtime; type is SomeChildType?

在Swift语言中,使用“as”做类型转换与正常的类型声明,对于编译器来说是一样的。例如,如下所示的演示代码。

    let y1 = x as SomeType  // Type information from 'as'
    let y2: SomeType = x   // Type information from an annotation

在Swift语言中,“as”运算符在“运行时(runtime)”会做检查,如果成功则会返回true,否则返回false。上述检查在“编译时”不能使用,例如下面的使用是错误的。

    "hello" is String
    "hello" is Int

6.6 主表达式

在Swift语言中,主表达式是最基本的表达式,可以与前缀表达式、二元表达式、后缀表达式以及其他主要表达式组合使用。

6.6.1 字符型表达式

在Swift语言中,字符型表达式由普通的字符(string、number)、一个字符的字典或者数组、或者表6-1中的特殊字符等内容组成。

表6-1 特殊字符

在Swift语言的某个函数(function)中,__FUNCTION__会返回当前函数的名字。在某个方法(method)中,会返回当前方法的名字。在某个property 的getter/setter中,会返回这个属性的名字。在init/subscript中,只有特殊成员(member)中会返回这个keyword的名字。在某个文件的顶端(the top level of a file),返回的是当前module的名字。

在Swift语言中,一个array literal是一个有序的值的集合,其具体形式如下所示。

    [value 1, value 2, ...]

在上述格式中,数组中的最后一个表达式可以紧跟一个逗号“,”,“[]”表示空数组。array literal的type是 T[],这个T就是数组中元素的type。如果该数组中有多种type,T则是与这些type的公共supertype最接近的type。

在Swift语言中,一个dictionary literal是一个包含无序的键值对(key-value pairs)的集合,其具体形式如下所示。

    [key 1: value 1, key 2: value 2, ...]

在上述格式中,dictionary的最后一个表达式可以是一个逗号“,”,“[:]”表示一个空的dictionary,它的type是 Dictionary,这里KeyType表示 key的type, ValueType表示 value的type。如果这个dictionary 中包含多种 types,那么“KeyType, Value”则对应着它们的公共supertype最接近的type。

6.6.2 self表达式

在Swift语言中,self表达式是对当前type 或者当前instance的引用,其具体形式如下所示。

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

如果在 initializer、subscript、instance method中,self等同于当前type的instance。在一个静态方法(static method)和类方法(class method)中,self等同于当前的type。

在Swift语言中,当访问 member(成员变量时),self 用来区分重名变量(如函数的参数)。例如在下面的演示代码中,self.greeting指的是var greeting: String,而不是init(greeting: String)。

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

在mutating 方法中,可以使用self对该instance进行赋值。例如,如下所示的演示代码。

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

6.6.3 超类表达式

在Swift语言中,通过使用超类表达式可以在某个class中访问它的超类,其具体形式如下所示。

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

在上述格式中,形式1用来访问超类的某个成员(member),形式2用来访问该超类的subscript实现。形式3 用来访问该超类的initializer。

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

6.6.4 闭包表达式

在Swift语言中,闭包(closure)表达式可以建立一个闭包(在其他语言中也叫 lambda,或者匿名函数(anonymous function))。声明闭包的方式与声明函数(function)的声明一样,闭包(closure)包含了可执行的代码(与方法主体(statement)类似)以及接收(capture)的参数。其具体形式如下所示。

    { (parameters) -> return type in
        statements
    }

在Swift语言中,闭包可以省略它的参数的type和返回值的type,如果省略了参数和参数类型,就也要省略“in”关键字。如果被省略的type无法被编译器获知(inferred),那么就会抛出编译错误。闭包可以省略参数,转而在方法体(statement)中使用 0,1, $2 来引用出现的第一个、第二个、第三个参数。

在Swift语言中,如果闭包中只包含了一个表达式,那么该表达式就会自动成为该闭包的返回值。在执行 'type inference '时,该表达式也会返回。例如下面几个闭包表达式是等价的。

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

在Swift语言中,闭包表达式可以通过一个参数列表(capture list)来显式指定它需要的参数。参数列表由中括号“[]”括起来,里面的参数由逗号“,”分隔。一旦使用了参数列表,就必须使用“in”关键字。在任何情况下都得这样做,包括忽略参数的名字、type和返回值时等。

在Swift语言中,在闭包的参数列表(capture list)中,参数可以声明为“weak”或者“unowned”。例如,如下所示的演示代码。

    myFunction { print(self.title) }                // strong capture
    myFunction { [weak self] in print(self!.title) }    // weak capture
    myFunction { [unowned self] in print(self.title) }    // unowned capture

在Swift语言中,在参数列表中可以使用任意表达式来赋值。该表达式会在闭包被执行时赋值,然后按照不同的力度来获取(这句话请慎重理解)。例如,如下所示的演示代码。

    // Weak capture of "self.parent" as "parent"
    myFunction { [weak parent = self.parent] in print(parent!.title) }

6.6.5 隐式成员表达式

在 Swift 语言中,在可以判断出类型(type)的上下文(context)中,隐式成员表达式是访问某个type的member(如 class method, enumeration case) 的简洁方法。其具体语法形式如下所示。

    .member name

例如,如下所示的演示代码。

    var x = MyEnumeration.SomeValue
    x = .AnotherValue

6.6.6 圆括号表达式

在 Swift 语言中,圆括号表达式由多个子表达式和逗号“,”组成,每个子表达式前面可以有“identifier x:”这样的可选前缀。其具体形式如下所示。

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

在上述格式中,圆括号表达式用来建立 tuples,然后把它作为参数传递给 function. 如果某个圆括号表达式中只有一个子表达式,那么它的type就是子表达式的type。例如(1)的 type是Int,而不是(Int)。

6.6.7 通配符表达式

在Swift语言中,通配符表达式用来忽略传递进来的某个参数。例如在下面的演示代码中,10被传递给x,20被忽略。

    (x, _) = (10, 20)
    // x is 10, 20 is ignored

6.7 后缀表达式

在Swift语言中,后缀表达式就是在某个表达式的后面加上操作符。严格地讲,每个主要表达式(primary expression)都是一个后缀表达式。Swift标准库组件提供了如下所示的后缀表达式。

□ ++ Increment。

□ -- Decrement。

6.7.1 函数调用表达式

在Swift语言中,函数调用表达式由函数名和参数列表组成,其具体形式如下所示。

    function name(argument value 1, argument value 2)

在上述语法格式中,如果该function 的声明中指定了参数的名字,那么在调用的时候也必须得写出来。例如,如下所示的演示代码。

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

可以在函数调用表达式的尾部(最后一个参数之后)加上一个闭包(closure),该闭包会被目标函数理解并执行。它具有如下两种写法:

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

如果闭包是该函数的唯一参数,那么圆括号可以省略。例如,如下所示的演示代码。

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

6.7.2 初始化函数表达式

在Swift语言中,Initializer表达式用来给某个Type初始化,其具体形式如下所示。

    expression.init(initializer arguments)

在上述格式中,Initializer表达式用来给某个Type初始化。与函数(function)不同,initializer不能返回值。例如,如下所示的演示代码。

    var x = SomeClass.someClassFunction     // ok
    var y = SomeClass.init            // error

可以通过 initializer 表达式来委托调用(delegate to )到superclass的initializers,例如,如下所示的演示代码。

    class SomeSubClass: SomeSuperClass {
        init() {
          // subclass initialization goes here
          super.init()
        }
    }

6.7.3 显式成员表达式

在Swift语言中,显示成员表达式允许我们访问type、tuple、module的成员变量,其具体形式如下所示。

    expression.member name

在上述格式中,该member 就是某个type在声明时所定义(declaration or extension)的变量,例如,如下所示的演示代码。

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

对于tuple,要根据它们出现的顺序(0, 1, 2...)来使用,例如,如下所示的演示代码。

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

6.7.4 后缀self表达式

在Swift语言中,后缀表达式由某个表达式 +‘.self’组成,其具体形式如下所示。

    expression.self
    type.self

□ 形式1 表示会返回 expression 的值。例如:x.self 返回 x。

□ 形式2:返回对应的type。我们可以用它来动态地获取某个instance的type。

6.7.5 动态表达式

在Swift语言中,dynamicType 表达式由某个表达式+‘.dynamicType’组成,其具体形式如下所示。

    expression.dynamicType

在上面的形式中,expression 不能是某 type 的名字。动态类型表达式会返回“运行时”某个instance的type,具体请看下面的列子。

    class SomeBaseClass {
        class func printClassName() {
          println("SomeBaseClass")
        }
    }
    class SomeSubClass: SomeBaseClass {
        override class func printClassName() {
          println("SomeSubClass")
        }
    }
    let someInstance: SomeBaseClass = SomeSubClass()
    // someInstance is of type SomeBaseClass at compile time, but
    // someInstance is of type SomeSubClass at runtime
    someInstance.dynamicType.printClassName()
    // prints "SomeSubClass"

6.7.6 下标脚本表达式

在 Swift 语言中,下标脚本表达式提供了通过下标脚本访问 getter/setter 的方法,其具体形式如下所示。

    expression[index expressions]

可以通过下标脚本表达式通过getter获取某个值,或者通过setter赋予某个值。

6.7.7 强制取值表达式

在Swift语言中,强制取值表达式用来获取某个目标表达式的值(该目标表达式的值必须不是nil ),其具体形式如下所示。

    expression!

如果该表达式的值不是nil则返回对应的值,否则抛出运行时错误(runtime error)。

6.7.8 可选链表达式

在Swift语言中,可选链表达式由目标表达式 +‘?’组成,其具体形式如下所示。

    expression?

在上述语法格式中,后缀“?”用于返回目标表达式的值,把它作为可选的参数传递给后续的表达式。如果某个后缀表达式包含了可选链表达式,那么它的执行过程就比较特殊,具体说明如下所示。

(1)首先先判断该可选链表达式的值,如果是 nil,,整个后缀表达式都返回nil。

(2)如果该可选链的值不是nil,则正常返回该后缀表达式的值(依次执行它的各个子表达式)。

在上述两种情况下,该后缀表达式仍然是一个optional type。

如果某个“后缀表达式”的“子表达式”中包含了“可选链表达式”,那么只有最外层的表达式返回的才是一个 optional type。例如,在下面的例子中,如果 c 不是 ni,那么c?.property.performAction()这句代码在执行时,就会先获得 c 的 property 方法,然后调用performAction()方法。然后对于 “c?.property.performAction()”这个整体来说,它的返回值是一个optional type。例如,如下所示的演示代码。

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

如果不使用可选链表达式,那么上面例子中的演示代码与下面代码完全等价。

    if let unwrappedC = c {
        result = unwrappedC.property.performAction()
    }