- Swift开发实战
- 管蕾 张玲玲 朱元波编著
- 162字
- 2024-12-22 05:54:40
第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() }