Swift表达式参考
前缀表达和二分表达可用来在简短语句中使用运算符。简单表达从概念上讲是获取值的最简单方式。后缀表达和前缀表达和二分表达相似,都可以让你建立更为复杂的表达方式;例如函数调用和成员获取等。我们将在本章节中详细解释每种表达方式。
GRAMMAR OF AN EXPRESSION
expression → prefix-expressionbinary-expressionsopt
expression-list → expression expression,expression-list
前缀表达的使用
前缀表达在表达式的前面加入一个前缀运算符。前缀运算符后面紧跟表达式,这个表达式可以理解为前缀运算符的参数。Swift的标准库提供了如下前缀操作符:
- ++ 自增
- -- 自减
- ! 逻辑非
- ~ 位运算取反
- + 加
- - 减
这些运算符的使用方法请参考Basic Operators 和 Advanced Operators 相关章节。
除了上面的标准库运算符,当一个变量作为参数在函数调用中传递时,& 可以放在变量名前面,这种参数称为In-Out Parameters。
GRAMMAR OF A PREFIX EXPRESSION
prefix-expression → prefix-operatoroptpostfix- expression
prefix-expression → in-out-expression
in-out-expression → &identifier
二分表达的使用
二分表达是一种将参数放于运算符两边的表达方式,它的形式如下:left-hand argument operator right-hand argument
Swift 标准库提供了如下二分操作符:
1) 幂(无左右相关,优先级 160)
- << 左移
- >> 右移
2) 乘除法(左相关,优先级150)
- * 乘
- / 除
- % 模
- &* 乘(忽略溢出)
- &/ 除(忽略溢出)
- &% 模(忽略溢出)
- & 位运算和
3) 加减法(左相关,优先级140)
- + 加
- - 减
- &+ 溢出加
- &- 溢出减
- | 或
- ^ 亦或
4) 区间(无左右相关,优先级135)
- .. 半开区间
- ... 闭区间
5) 转换(无左右相关,优先级132)
- is 类型检查
- as 类型转换
6) 比较符(无左右相关性,优先级130)
- < 小于
- <= 小于等于
- > 大于
- >= 大于等于
- == 等于
- != 不等于
- === 相同
- !== 不相同
- ~= 模式匹配
6) 连接符(左相关,优先级120)
- && 逻辑与
- || 逻辑或
7) 三元运算符(右相关,优先级100)
- ? 三元运算条件符
8) 赋值运算符(右相关,优先级90)
- = 赋值
- *= 乘并赋值
- /= 除并赋值
- %= 取模并赋值
- += 自加并赋值
- -= 自减并赋值
- <<= 左移并赋值
- >>= 右移并赋值
- &= 位运算和并赋值
- ^= 位运算亦或并赋值
- |= 位运算或并赋值
- &&= 逻辑和并赋值
- || 逻辑或并赋值
这些操作符的使用方法,请参照 基本运算符和高级运算符。
二分表达式的语法:注意: 在解析的时候,二分运算符组成的表达式将被拆解为一个列表的形式。这个列表将被转化为一个序列来计算。例如,`2 + 3 * 5` 最开始被理解为一个有五个元素的列表:`2`,`+`,`3`,`*`, `5`。然后才转换为序列的形式(2+(3*5))。
binary-expression → binary-operatorprefix-expression
binary-expression → assignment-operatorprefix-expression
binary-expression → conditional-operatorprefix-expression
binary-expression → type-casting-operator
binary-expressions → binary-expressionbinary-expressionso
赋值运算符的使用
赋值运算符为表达式赋予新值。其表达方式如下:表达式 = 值
通过这个表达式,右侧的值将赋予左侧的表达式。如果左侧是一个元素组合(tuple),右侧的值必须与其具有相同额元素个数(允许多层组合)。赋值的操作和每一部分的元素一一对应。例如
(a, _, (b, c)) = ("test", 9.45, (12, 3)) // a 是 "test", b 是 12, c 是 3, 9.45 被自动忽略了。赋值运算符没有返回值。
三元运算符的使用
条件表达式 ?如果为真则执行本表达式:如果为假则执行本表达式如果条件表达式为真,则执行第一个表达式并返回它的值,否则将执行第二个表达式并返回它的值。没有使用的表达式不会被执行。
更多的例子参照三木运算符。
类型转换运算符的使用
一共有两种类型转换运算符:as 和 is,它们的形式如下:- 表达式 as 类型
- 表达式 as? 类型
- 表达式 is 类型
如果这种向特定类型的转换成功,那么这个表达式将被返回此特定类型的一个实例。典型的例子是向上转型。
如果这种像特定类型的转换失败,那么将引发编译期错误。
如果在编译期转换成功与否不能确定,这个转换表达式将变为可选值类型。在运行期,如果转换成功,表达式的值将会打包成一个可选值并返回,否则,此值将为空,典型的例子为向下类型转换。
class SomeSuperType {} class SomeType: SomeSuperType {} class SomeChildType: SomeType {} let s = SomeType() let x = s as SomeSuperType // 成功,则返回值 SomeSuperType let y = s as Int // 失败,编译期错误 let z = s as SomeChildType // 可能在运行期失败,返回值类型为SomeChildType?对编译器来说,使用 ```as```来确定类型 和 类型声明 具有相同的作用。下面的例子可以说明:
let y1 = x as SomeType // 使用as表明类型 let y2: SomeType = x // 使用类型声明`is`操作符将字运行时检查表达式是否为特定类型。如果是则返回真(`true`),否则返回假(`false`)。
在编译期检查的结果必须不确定,下面的例子是不合法的:
“hello" is String "hello" is Int更多关于类型转换的解释和例子请参考 [类型转换](for linkage)。
简单表达式的使用
简单表达是最基础的一类表达形式。他们可以单独使用或与其他符号一起使用来组成前缀表达,二分表达,以及后缀表达。GRAMMAR OF A PRIMARY EXPRESSION
primary-expression → identifiergeneric-argument-clauseopt
primary-expression → literal-expression
primary-expression → self-expression
primary-expression → superclass-expression
primary-expression → closure-expression
primary-expression → parenthesized-expression
primary-expression → implicit-member-expression
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方法中它就是这个属性的名字;在特殊成员例如`init`和`subscript`中,它是关键字的名字;在文件的顶层,它是当前模型名。
数组的文字声明是一个包含有序值的集合。其结构如下:
[`value1`, `value2`, `...` ]
数组内最后一个表达式可以跟句号。一个空数组声明可以用一个空的中括号代表([])。数组值的类型为`T[]`,`T`代表数组内部表达式的类型。如果其中的元素类型不统一,T代表他们最小超类。
字典的文字声明是一个有键值对的无序集合。它有如下表达:
[`key`:`value1`, `key`:`value2`, `...` ]
字典内最后一个表达式可以跟句号。一个空字典的声明是中括号内加一个冒号([:])以此和空数组的表达形式区分。
字典的类型为`Dictionary<keytype, ValueType>`,`keytype`是键的类型,`ValueType`是其对应值的类型。如果字典有多种类型,`keytype`和`ValueType`使他们代表值类型的最小超类。
GRAMMAR OF A LITERAL EXPRESSION
literal-expression → literal
literal-expression → array-literal dictionary-literal
literal-expression → __FILE__ __LINE__ __COLUMN__ __FUNCTION__
array-literal → [array-literal-itemsopt]
array-literal-items → array-literal-item,opt array-literal-item,array-literal-items
array-literal-item → expression
dictionary-literal → [dictionary-literal-items] [:]
dictionary-literal-items → dictionary-literal-item,opt dictionary-literal-item,dictionary-literal-items
dictionary-literal-item → expression:expression
Self的使用
`self`是对当前类型或当前类的实例的一种显式引用。其使用方法如下:self self.`member name` self[`subscript index`] self(`initializer arguments`) self.init(`initializer arguments`)在构造器,子脚本或实例方法中,`self`指当前实例的引用。在静态方法或类方法中,`self`指当前类的引用。
`self`用于当同一作用域存在同名变量的时候来明确指代以访问成员变量,例如临时变量与成员变量同名的情况:
class SomeClass { var greeting: String init(greeting: String) { self.greeting = greeting } }在一个mutating类型值的方法中,你可以为一个新的实例赋值为self,例如:
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) } }
GRAMMAR OF A SELF EXPRESSION
self-expression → self
self-expression → self.identifier
self-expression → self[expression]
self-expression → self.init
超类的使用
超类的表达式可以让一个类与它的超类产生关联。如下例:super.`member name` super[`subscript index`] super.init(`initializer arguments`)第一种形式可以获取超类的成员。第二种形式可以获取超类的角标方法实现。第三种形式可以用来访问超类的初始化器。
子类使用超类来实现他们自己的成员,子脚本和初始化器。
GRAMMAR OF A SUPERCLASS EXPRESSION
superclass-expression → superclass-method-expression superclass-subscript-expression superclass-initializer-expression
superclass-method-expression → super.identifier
superclass-subscript-expression → super[expression]
superclass-initializer-expression → super.init
闭包的使用
闭包创建一个闭合区间,在其他语言中,也被称为lambda或匿名函数。像方法声明一样,闭包也包含供其执行的语句并从此闭合区间取值。它的形式如下:{ (`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 }
关于闭包在函数中传参的问题可参考 [函数调用的使用](for link)。
使用抓取列表(capture list),闭包可以显式的得到特定值。抓取列表内容由中括号括起,由逗号分隔,后面是参数列表。当你使用抓取列表时,即使你省略参数名,参数类型和返回值类型,你也必须写`in`关键字。
抓取列表的每个入口可以标明`weak`或`unowned`来抓取值的弱引用或无主引用。
myFunction { print(self.title) } // strong capture myFunction { [weak self] in print(self!.title) } // weak capture你也可以向其中的非匿名值加入判断表达,当闭包执行时,将对表达式做出判断,并使用特定的引用强度抓取。例如:
// Weak capture of "self.parent" as "parent" myFunction { [weak parent = self.parent] in print(parent!.title) }更多闭包的信息和实例,请参考[闭包](for link)。
GRAMMAR OF A CLOSURE EXPRESSION
closure-expression → {closure-signatureoptstatements} closure-signature → parameter-clausefunction-resultoptin closure-signature → identifier-listfunction-resultoptin closure-signature → capture-listparameter-clausefunction-resultoptin closure-signature → capture-listidentifier-listfunction-resultoptin closure-signature → capture-listin capture-list → [capture-specifierexpression] capture-specifier → weak unowned unowned(safe) unowned(unsafe)
匿名成员的使用
匿名成员是一种访问成员的简单形式,例如在枚举类型或类方法的上下文中,引用类型可推断时使用。它有如下形式:.`member name`
例如:
var x = MyEnumeration.SomeValue x = .AnotherValue
GRAMMAR OF A IMPLICIT MEMBER EXPRESSION
implicit-member-expression → .identifier
括号表达式的使用
括号表达式内部表达式用逗号分隔,外部用括号括起。每个表达式内部可以有一个表达式名,它与表达式用冒号分隔。其格式如下:
(`identifier 1`: `expression 1`, `identifier 2`: `expression 2`,` ...`)
利用括号表达式我们可以为函数调用传参。如果括号内只有一个值,其括号表达式的类型即为此值的类型。例如(1)的类型是`Int`,不是`(Int)`。
GRAMMAR OF A PARENTHESIZED EXPRESSION
parenthesized-expression → (expression-element-listopt) expression-element-list → expression-element expression-element,expression-element-list expression-element → expression identifier:expression
占位符表达式的使用
占位符表达是为了在赋值时故意忽略掉一个值。例如在下面的例子中,10被赋给`x`,20则被忽略。(x, _) = (10, 20)
GRAMMAR OF A WILDCARD EXPRESSION
wildcard-expression → _
后修饰表达式的使用
使用后修饰运算符或其他后修饰语法可以创建后修饰表达式。从语法上讲,所有的简单表达式也是后修饰表达式。Swift标准库提供了以下后修饰运算符:
- ++ 自加
- -- 自减
关于此运算符的使用,请参考[基本操作符]() 和[高级操作符]();
GRAMMAR OF A POSTFIX EXPRESSION
postfix-expression → primary-expression postfix-expression → postfix-expressionpostfix-operator postfix-expression → function-call-expression postfix-expression → initializer-expression postfix-expression → explicit-member-expression postfix-expression → postfix-self-expression postfix-expression → dynamic-type-expression postfix-expression → subscript-expression postfix-expression → forced-value-expression 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`)
函数的调用时可以在闭合括号的后面紧跟一个闭包后缀。这个后缀代码块也是这个函数的后加的参数。下面的两个函数调用等效:
// someFunction 的参数列表由一个参数x和一个闭包组成 someFunction(x, {$0 == 13}) someFunction(x) {$0 == 13}
如果闭包是唯一参数,小括号可以省略。
// someFunction 只有一个参数 myData.someMethod() {$0 == 13} myData.someMethod {$0 == 13}
GRAMMAR OF A FUNCTION CALL EXPRESSION
function-call-expression → postfix-expressionparenthesized-expression function-call-expression → postfix-expressionparenthesized-expressionopttrailing-closure trailing-closure → closure-expression
初始化器表达式的用法
初始化表达式将访问该类型的初始化器。其结构如下:`expression`.init(`initializer arguments`)
初始化器的调用方法与函数调用相同,但与函数不同的是,初始化器不是一个值。例如:
var x = SomeClass.someClassFunction // 正确 var y = SomeClass.init // 错误你也可以使用初始化器来调用超类的初始化器。
class SomeSubClass: SomeSuperClass { init() { // subclass initialization goes here super.init() } } GRAMMAR OF AN INITIALIZER EXPRESSION initializer-expression → postfix-expression.init
显式成员表达式的使用
显式成员表达式可以访问有名的类,元组,或组件的成员。在表达式和成员名之间用点隔开。`expression`.`member name`
有名成员变量可作为类的声明的一部分或其拓展。例如:
class SomeClass { var someProperty = 42 } let c = SomeClass() let y = c.someProperty // Member access元组的命名是用数字由零开始按顺序隐式命名的。例如:
var t = (10, 20, 30) t.0 = t.1 // 现在 t 是 (20, 20, 30)
组件成员可以获取其最顶层声明。
GRAMMAR OF AN EXPLICIT MEMBER EXPRESSION
explicit-member-expression → postfix-expression.decimal-digit explicit-member-expression → postfix-expression.identifiergeneric-argument-clauseopt
self的后缀表达方式的使用
self的后缀表达使用是用表达式或类型名加“.self” 构成的。其形式如下:`expression`.self
`type`.self
第一种形式得到表达式的值。例如x.self就是x值。
第二种形式将获得类型的值。利用这种形式可以以值的形式获得类型。例如。SomeClass.self就是SomeClass类,你可以将其传递给接受类类型参数的函数或方法。
GRAMMAR OF A SELF EXPRESSION
postfix-self-expression → postfix-expression.self
动态类型表达式的使用
动态类型表达式由表达式加“.dynamicType”构成。其结构如下:`expression`.dynamicType
表达式不能是类名。完整的dynamicType表达式获得表达式的运行时类名值。如下例:
class SomeBaseClass { class func printClassName() { println("SomeBaseClass") } } class SomeSubClass: SomeBaseClass { override class func printClassName() { println("SomeSubClass") } } let someInstance: SomeBaseClass = SomeSubClass() // someInstance的编译时类型是SomeBaseClass , 但是 // someInstance的运行时类型是SomeSubClass someInstance.dynamicType.printClassName() // 打印 "SomeSubClass"
GRAMMAR OF A DYNAMIC TYPE EXPRESSION
dynamic-type-expression → postfix-expression.dynamicType
亚脚本表达式的使用
亚脚本表达式使用对应声明的get方法个set方法来获取亚脚本。它的格式如下:`expression`[`index expressions`]
为了获取亚脚本表达式的值,将亚脚本表达方式作为参数调用角标的get方法。当然,为了设置它的值,亚脚本的set方法也用同样的方式调用。
更多关于亚脚本声明,请参见[亚脚本声明]()。
GRAMMAR OF A SUBSCRIPT EXPRESSION
subscript-expression → postfix-expression[expression-list]
强制拆包表达式的使用
当一个可选值不为空时,可以对可选值进行强制拆包。 它的结构如下:`expression`!
自判断链接表达式的使用
自判断链接通过使用可选值加后缀,使用非常简单。它的结构如下:`expression`.?
后缀?将以可选值的形式返回其参数。
在后缀表达中,自判断链接的取值方式很特别。如果自判断链接为空,后面其他的表达式不会被执行且整个表达式返回空。如果自判断链接结果不是空,表达式将被拆包并用继续执行余下的后缀表达式。不论何种情况,后缀表达式的值都是一个可选值类型。
如果自判断链接是嵌套的,只返回最外层的的可选值类型。在下面的例子中,如果`c`不是nil,它的值将被拆包,并用以获得`.property` 和 `.performAction()`的值。整个`c?.property.performAction() `是可选值类型。
var c: SomeClass?
var result: Bool? = c?.property.performAction()
下面的例子则示意了不使用自判断链接的情形。
if let unwrappedC = c { result = unwrappedC.property.performAction() }
GRAMMAR OF AN OPTIONAL-CHAINING EXPRESSION
optional-chaining-expression → postfix-expression?