Swift中的表达法

在Swift中,一共有四种表达法:前缀表达,二分表达,简单表达,和后缀表达。Evaluating an expression returns a value, causes a side effect, or both.

前缀表达和二分表达可用来在简短语句中使用运算符。简单表达从概念上讲是获取值的最简单方式。后缀表达和前缀表达和二分表达相似,都可以让你建立更为复杂的表达方式;例如函数调用和成员获取等。我们将在本章节中详细解释每种表达方式。

  1. GRAMMAR OF AN EXPRESSION
  2. expression prefix-expressionbinary-expressionsopt
  3. expression-list expression expression,expression-list

前缀表达的使用

前缀表达在表达式的前面加入一个前缀运算符。前缀运算符后面紧跟表达式,这个表达式可以理解为前缀运算符的参数。

Swift的标准库提供了如下前缀操作符:

  • ++ 自增
  • -- 自减
  • ! 逻辑非
  • ~ 位运算取反
  • +
  • -

这些运算符的使用方法请参考Basic OperatorsAdvanced Operators 相关章节。

除了上面的标准库运算符,当一个变量作为参数在函数调用中传递时,& 可以放在变量名前面,这种参数称为In-Out Parameters。

  1. GRAMMAR OF A PREFIX EXPRESSION
  2. prefix-expression prefix-operatoroptpostfix- expression
  3. prefix-expression in-out-expression
  4. in-out-expression &identifier

二分表达的使用

二分表达是一种将参数放于运算符两边的表达方式,它的形式如下:

left-hand argument operator right-hand argument

Swift 标准库提供了如下二分操作符:

  • 幂(无左右相关,优先级 160)

    • << 左移
    • >> 右移
  • 乘除法(左相关,优先级150)

    • *
    • /
    • %
    • &* 乘(忽略溢出)
    • &/ 除(忽略溢出)
    • &% 模(忽略溢出)
    • & 位运算和
  • 加减法(左相关,优先级140)

    • +
    • -
    • &+ 溢出加
    • &- 溢出减
    • |
    • ^ 亦或
  • 区间(无左右相关,优先级135)

    • .. 半开区间
    • ... 闭区间
  • 转换(无左右相关,优先级132)

    • is 类型检查
    • as 类型转换
  • 比较符(无左右相关性,优先级130)

    • < 小于
    • <= 小于等于
    • > 大于
    • >= 大于等于
    • == 等于
    • != 不等于
    • === 相同
    • !== 不相同
    • ~= 模式匹配
  • 连接符(左相关,优先级120)

    • && 逻辑与
  • 连接符(左相关,优先级120)

    • || 逻辑或
  • 三元运算符(右相关,优先级100)

    • 三元运算条件符
  • 赋值运算符(右相关,优先级90)

    • = 赋值
    • *= 乘并赋值
    • /= 除并赋值
    • %= 取模并赋值
    • += 自加并赋值
    • -= 自减并赋值
    • <<= 左移并赋值
    • >>= 右移并赋值
    • &= 位运算和并赋值
    • ^= 位运算亦或并赋值
    • |= 位运算或并赋值
    • &&= 逻辑和并赋值
    • || 逻辑或并赋值

      这些操作符的使用方法,请参照 基本运算符和高级运算符

      1. 注意:
      2. 在解析的时候,二分运算符组成的表达式将被拆解为一个列表的形式。这个列表将被转化为一个序列来计算。例如,`2 + 3 * 5` 最开始被理解为一个有五个元素的列表:`2``+``3``*` `5`。然后才转换为序列的形式(2+(3*5))。
  1. 二分表达式的语法
  2. binary-expression binary-operatorprefix-expression
  3. binary-expression assignment-operatorprefix-expression
  4. binary-expression conditional-operatorprefix-expression
  5. binary-expression type-casting-operator
  6. binary-expressions binary-expressionbinary-expressionso

赋值运算符的使用

赋值运算符为表达式赋予新值。其表达方式如下:

表达式 =

通过这个表达式,右侧的值将赋予左侧的表达式。如果左侧是一个元素组合(tuple),右侧的值必须与其具有相同额元素个数(允许多层组合)。赋值的操作和每一部分的元素一一对应。例如

  1. (a, _, (b, c)) = ("test", 9.45, (12, 3))
  2. // a 是 "test", b 是 12, c 是 3, 9.45 被自动忽略了。

赋值运算符没有返回值。

三元运算符的使用

条件表达式如果为真则执行本表达式如果为假则执行本表达式

如果条件表达式为真,则执行第一个表达式并返回它的值,否则将执行第二个表达式并返回它的值。没有使用的表达式不会被执行。

更多的例子参照三木运算符

类型转换运算符的使用

一共有两种类型转换运算符:asis,它们的形式如下:

表达式 as 类型

表达式 as? 类型

表达式 is 类型

  1. * 如果这种向特定类型的转换成功,那么这个表达式将被返回此特定类型的一个实例。典型的例子是向上转型。
  2. * 如果这种像特定类型的转换失败,那么将引发编译期错误。
  3. * 如果在编译期转换成功与否不能确定,这个转换表达式将变为可选值类型。在运行期,如果转换成功,表达式的值将会打包成一个可选值并返回,否则,此值将为空,典型的例子为向下类型转换。
  1. class SomeSuperType {}
  2. class SomeType: SomeSuperType {}
  3. class SomeChildType: SomeType {}
  4. let s = SomeType()
  5. let x = s as SomeSuperType
  6. // 成功,则返回值 SomeSuperType
  7. let y = s as Int
  8. // 失败,编译期错误
  9. let z = s as SomeChildType
  10. // 可能在运行期失败,返回值类型为SomeChildType?
  11. ```

对编译器来说,使用 as来确定类型 和 类型声明 具有相同的作用。下面的例子可以说明:

  1. let y1 = x as SomeType // 使用as表明类型
  2. let y2: SomeType = x // 使用类型声明

is操作符将字运行时检查表达式是否为特定类型。如果是则返回真(true),否则返回假(false)。

在编译期检查的结果必须不确定,下面的例子是不合法的:

  1. hello" is String
  2. "hello" is Int

更多关于类型转换的解释和例子请参考 类型转换

简单表达式的使用

简单表达是最基础的一类表达形式。他们可以单独使用或与其他符号一起使用来组成前缀表达,二分表达,以及后缀表达。

  1. GRAMMAR OF A PRIMARY EXPRESSION
  2. primary-expression identifiergeneric-argument-clauseopt
  3. primary-expression literal-expression
  4. primary-expression self-expression
  5. primary-expression superclass-expression
  6. primary-expression closure-expression
  7. primary-expression parenthesized-expression
  8. primary-expression implicit-member-expression
  9. primary-expression wildcard-expression

文字表达形式的使用

文字表达由普通的文字(例如字符串或数字),数组或字典名,或者下面的特别文字

Literal Type Value
FILE String The name of the file in which it appears
LINE Int The line number on which it appears.
COLUMN Int The column number in which it begins.
FUNCTION String The name of the declaration in which it appears.

在一个函数内部,FUNCTION就是这个函数的名字;在方法内部,它就是方法名;在属性的get方法和set方法中它就是这个属性的名字;在特殊成员例如initsubscript中,它是关键字的名字;在文件的顶层,它是当前模型名。

数组的文字声明是一个包含有序值的集合。其结构如下:

[value1, value2, ... ]

数组内最后一个表达式可以跟句号。一个空数组声明可以用一个空的中括号代表([])。数组值的类型为T[]T代表数组内部表达式的类型。如果其中的元素类型不统一,T代表他们最小超类。

字典的文字声明是一个有键值对的无序集合。它有如下表达:

[key:value1, key:value2, ... ]

字典内最后一个表达式可以跟句号。一个空字典的声明是中括号内加一个冒号([:])以此和空数组的表达形式区分。

字典的类型为Dictionary<keytype, ValueType>keytype是键的类型,ValueType是其对应值的类型。如果字典有多种类型,keytypeValueType使他们代表值类型的最小超类。

  1. GRAMMAR OF A LITERAL EXPRESSION
  2. literal-expression literal
  3. literal-expression array-literal dictionary-literal
  4. literal-expression __FILE__ __LINE__ __COLUMN__ __FUNCTION__
  5. array-literal [array-literal-itemsopt]
  6. array-literal-items array-literal-item,opt array-literal-item,array-literal-items
  7. array-literal-item expression
  8. dictionary-literal [dictionary-literal-items] [:]
  9. dictionary-literal-items dictionary-literal-item,opt dictionary-literal-item,dictionary-literal-items
  10. dictionary-literal-item expression:expression

Self的使用

self是对当前类型或当前类的实例的一种显式引用。其使用方法如下:

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

在构造器,子脚本或实例方法中,self指当前实例的引用。在静态方法或类方法中,self指当前类的引用。

self用于当同一作用域存在同名变量的时候来明确指代以访问成员变量,例如临时变量与成员变量同名的情况:

  1. class SomeClass {
  2. var greeting: String
  3. init(greeting: String) {
  4. self.greeting = greeting
  5. }
  6. }

在一个mutating类型值的方法中,你可以为一个新的实例赋值为self,例如:

  1. struct Point {
  2. var x = 0.0, y = 0.0
  3. mutating func moveByX(deltaX: Double, y deltaY: Double) {
  4. self = Point(x: x + deltaX, y: y + deltaY)
  5. }
  6. }

GRAMMAR OF A SELF EXPRESSION

  1. self-expression self
  2. self-expression self.identifier
  3. self-expression self[expression]
  4. self-expression self.init

超类的使用

超类的表达式可以让一个类与它的超类产生关联。如下例:

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

第一种形式可以获取超类的成员。第二种形式可以获取超类的角标方法实现。第三种形式可以用来访问超类的初始化器。

子类使用超类来实现他们自己的成员,子脚本和初始化器。

  1. GRAMMAR OF A SUPERCLASS EXPRESSION
  2. superclass-expression superclass-method-expression superclass-subscript-expression superclass-initializer-expression
  3. superclass-method-expression super.identifier
  4. superclass-subscript-expression super[expression]
  5. superclass-initializer-expression super.init

闭包的使用

闭包创建一个闭合区间,在其他语言中,也被称为lambda或匿名函数。像方法声明一样,闭包也包含供其执行的语句并从此闭合区间取值。它的形式如下:

{ (parameters) -> return type in

statements
}

这里的参数和形式与函数的声明的形式相同。

它具有一些更简洁的写法:

  • 闭包可以不写它的参数类型,返回值类型。如果省略参数名和其类型,同时省略语句前的in关键字;如果无法推断其类型,则会产生编译时错误。
  • 如果闭包省略了它的参数名,它的参数将会隐式的以$加其位置编号命名:$0,$1,$2等等。
  • 只有一个表达式的闭包默认返回表达式的值。当周围有表达式操作引用类型时,这个表达式的内容也会参与其中。下面的几个闭包是等价的:
  1. myFunction {
  2. (x: Int, y: Int) -> Int in
  3. return x + y
  4. }
  5. myFunction {
  6. (x, y) in
  7. return x + y
  8. }
  9. myFunction { return $0 + $1 }
  10. myFunction { $0 + $1 }

关于闭包在函数中传参的问题可参考 函数调用的使用

使用抓取列表(capture list),闭包可以显式的得到特定值。抓取列表内容由中括号括起,由逗号分隔,后面是参数列表。当你使用抓取列表时,即使你省略参数名,参数类型和返回值类型,你也必须写in关键字。

抓取列表的每个入口可以标明weakunowned来抓取值的弱引用或无主引用。

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

你也可以向其中的非匿名值加入判断表达,当闭包执行时,将对表达式做出判断,并使用特定的引用强度抓取。例如:

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

更多闭包的信息和实例,请参考闭包

  1. GRAMMAR OF A CLOSURE EXPRESSION
  2. closure-expression {closure-signatureoptstatements}
  3. closure-signature parameter-clausefunction-resultoptin
  4. closure-signature identifier-listfunction-resultoptin
  5. closure-signature capture-listparameter-clausefunction-resultoptin
  6. closure-signature capture-listidentifier-listfunction-resultoptin
  7. closure-signature capture-listin
  8. capture-list [capture-specifierexpression]
  9. capture-specifier weak unowned unowned(safe) unowned(unsafe)

匿名成员的使用

匿名成员是一种访问成员的简单形式,例如在枚举类型或类方法的上下文中,引用类型可推断时使用。它有如下形式:

.member name

例如:

  1. var x = MyEnumeration.SomeValue
  2. x = .AnotherValue

GRAMMAR OF A IMPLICIT MEMBER EXPRESSION

  1. implicit-member-expression .identifier

括号表达式的使用

括号表达式内部表达式用逗号分隔,外部用括号括起。每个表达式内部可以有一个表达式名,它与表达式用冒号分隔。其格式如下:

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

利用括号表达式我们可以为函数调用传参。如果括号内只有一个值,其括号表达式的类型即为此值的类型。例如(1)的类型是Int,不是(Int)

  1. GRAMMAR OF A PARENTHESIZED EXPRESSION
  2. parenthesized-expression (expression-element-listopt)
  3. expression-element-list expression-element expression-element,expression-element-list
  4. expression-element expression identifier:expression

占位符表达式的使用

占位符表达是为了在赋值时故意忽略掉一个值。例如在下面的例子中,10被赋给x,20则被忽略。

  1. (x, _) = (10, 20)
  1. GRAMMAR OF A WILDCARD EXPRESSION
  2. wildcard-expression _

后修饰表达式的使用

使用后修饰运算符或其他后修饰语法可以创建后修饰表达式。从语法上讲,所有的简单表达式也是后修饰表达式。

Swift标准库提供了以下后修饰运算符:

  • ++ 自加
  • — 自减

关于此运算符的使用,请参考基本操作符高级操作符;

  1. GRAMMAR OF A POSTFIX EXPRESSION
  2. postfix-expression primary-expression
  3. postfix-expression postfix-expressionpostfix-operator
  4. postfix-expression function-call-expression
  5. postfix-expression initializer-expression
  6. postfix-expression explicit-member-expression
  7. postfix-expression postfix-self-expression
  8. postfix-expression dynamic-type-expression
  9. postfix-expression subscript-expression
  10. postfix-expression forced-value-expression
  11. postfix-expression optional-chaining-expression

函数调用的方法

函数的调用方法为:函数名家括号,括号内为参数列表,多个参数用逗号隔开。例如:

function name(argument value 1, argument value 2)

函数名可以以任意具有函数类型的名字来命名。

如果函数声明中定义了参数名,那么函数调用时,参数名必须在参数值前面,并用冒号分开,示例如下:

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

函数的调用时可以在闭合括号的后面紧跟一个闭包后缀。这个后缀代码块也是这个函数的后加的参数。下面的两个函数调用等效:

  1. // someFunction 的参数列表由一个参数x和一个闭包组成
  2. someFunction(x, {$0 == 13})
  3. someFunction(x) {$0 == 13}

如果闭包是唯一参数,小括号可以省略。

  1. // someFunction 只有一个参数
  2. myData.someMethod() {$0 == 13}
  3. myData.someMethod {$0 == 13}
  1. GRAMMAR OF A FUNCTION CALL EXPRESSION
  2. function-call-expression postfix-expressionparenthesized-expression
  3. function-call-expression postfix-expressionparenthesized-expressionopttrailing-closure
  4. trailing-closure closure-expression

初始化器表达式的用法

初始化表达式将访问该类型的初始化器。其结构如下:

expression.init(initializer arguments)

初始化器的调用方法与函数调用相同,但与函数不同的是,初始化器不是一个值。例如:

  1. var x = SomeClass.someClassFunction // 正确
  2. var y = SomeClass.init // 错误

你也可以使用初始化器来调用超类的初始化器。

  1. class SomeSubClass: SomeSuperClass {
  2. init() {
  3. // subclass initialization goes here
  4. super.init()
  5. }
  6. }
  1. GRAMMAR OF AN INITIALIZER EXPRESSION
  2. initializer-expression postfix-expression.init

显式成员表达式的使用

显式成员表达式可以访问有名的类,元组,或组件的成员。在表达式和成员名之间用点隔开。

expression.member name

有名成员变量可作为类的声明的一部分或其拓展。例如:

  1. class SomeClass {
  2. var someProperty = 42
  3. }
  4. let c = SomeClass()
  5. let y = c.someProperty // Member access

元组的命名是用数字由零开始按顺序隐式命名的。例如:

  1. var t = (10, 20, 30)
  2. t.0 = t.1
  3. // 现在 t 是 (20, 20, 30)

组件成员可以获取其最顶层声明。

  1. GRAMMAR OF AN EXPLICIT MEMBER EXPRESSION
  2. explicit-member-expression postfix-expression.decimal-digit
  3. explicit-member-expression postfix-expression.identifiergeneric-argument-clauseopt

self的后缀表达方式的使用

self的后缀表达使用是用表达式或类型名加“.self” 构成的。其形式如下:

expression.self type.self

第一种形式得到表达式的值。例如x.self就是x值。

第二种形式将获得类型的值。利用这种形式可以以值的形式获得类型。例如。SomeClass.self就是SomeClass类,你可以将其传递给接受类类型参数的函数或方法。

  1. GRAMMAR OF A SELF EXPRESSION
  2. postfix-self-expression postfix-expression.self

动态类型表达式的使用

动态类型表达式由表达式加“.dynamicType”构成。其结构如下:

expression.dynamicType

表达式不能是类名。完整的dynamicType表达式获得表达式的运行时类名值。如下例:

  1. class SomeBaseClass {
  2. class func printClassName() {
  3. println("SomeBaseClass")
  4. }
  5. }
  6. class SomeSubClass: SomeBaseClass {
  7. override class func printClassName() {
  8. println("SomeSubClass")
  9. }
  10. }
  11. let someInstance: SomeBaseClass = SomeSubClass()
  12. // someInstance的编译时类型是SomeBaseClass , 但是
  13. // someInstance的运行时类型是SomeSubClass
  14. someInstance.dynamicType.printClassName()
  15. // 打印 "SomeSubClass"
  1. GRAMMAR OF A DYNAMIC TYPE EXPRESSION
  2. dynamic-type-expression postfix-expression.dynamicType

亚脚本表达式的使用

亚脚本表达式使用对应声明的get方法个set方法来获取亚脚本。它的格式如下:

expression[index expressions]

为了获取亚脚本表达式的值,将亚脚本表达方式作为参数调用角标的get方法。当然,为了设置它的值,亚脚本的set方法也用同样的方式调用。

更多关于亚脚本声明,请参见亚脚本声明

  1. GRAMMAR OF A SUBSCRIPT EXPRESSION
  2. subscript-expression postfix-expression[expression-list]

强制拆包表达式的使用

当一个可选值不为空时,可以对可选值进行强制拆包。 它的结构如下:

expression

自判断链接表达式的使用

自判断链接通过使用可选值加后缀,使用非常简单。它的结构如下:

expression.?

后缀?将以可选值的形式返回其参数。

在后缀表达中,自判断链接的取值方式很特别。如果自判断链接为空,后面其他的表达式不会被执行且整个表达式返回空。如果自判断链接结果不是空,表达式将被拆包并用继续执行余下的后缀表达式。不论何种情况,后缀表达式的值都是一个可选值类型。

如果自判断链接是嵌套的,只返回最外层的的可选值类型。在下面的例子中,如果c不是nil,它的值将被拆包,并用以获得.property.performAction()的值。整个c?.property.performAction()是可选值类型。

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

  1. 下面的例子则示意了不使用自判断链接的情形。

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

GRAMMAR OF AN OPTIONAL-CHAINING EXPRESSION

‌ optional-chaining-expression → postfix-expression? ```