表达式
在Swift中,有四种表达式:前缀表达式,二进制表达式,主表达式和后缀表达式。 评估一个表达式会返回一个值,导致副作用,或者两者都有。
前缀和二进制表达式可让您将运算符应用于较小的表达式。 主要表达式在概念上是最简单的表达式,它们提供了访问值的方式。 Postfix表达式(如前缀和二进制表达式)允许使用后缀(例如函数调用和成员访问)构建更复杂的表达式。 以下各节详细介绍了各种表达方式。
GRAMMAR OF AN EXPRESSION
expression → try-operator-opt-prefix-expression-binary-expressions-opt-
expression-list → expression-expression-,-expression-list-
前缀表达式
前缀表达式将可选的前缀运算符与表达式结合在一起。 前缀运算符带有一个参数,即它们后面的表达式。
有关这些运算符行为的信息,请参阅基本运算符和高级运算符。
有关Swift标准库提供的运算符的信息,请参阅运算符声明。
除了标准库运算符之外,还可以使用&作为输入参数传递给函数调用表达式的变量的名称。 有关更多信息并查看示例,请参阅输入输出参数。
prefix-expression → prefix-operator-opt-postfix-expression-
prefix-expression → in-out-expression-
in-out-expression → &-identifier-
try运算符
try表达式由try运算符组成,后面跟着一个可以抛出错误的表达式。 它有以下形式:
tryexpression
一个可选的try表达式包含try? 运算符后跟可能会引发错误的表达式。 它有以下形式:
try? expression
如果表达式不会引发错误,则optional-try表达式的值是一个包含表达式值的可选项。 否则,可选try表达式的值为零。
强制try表达由try!运算符后跟可能会引发错误的表达式。 它有以下形式:
try! expression
如果表达式抛出错误,则会产生运行时错误。
当二进制运算符左侧的表达式用try标记时,try?或try!,则该运算符应用于整个二进制表达式。 也就是说,您可以使用括号来明确操作员应用程序的范围。
sum = try someThrowingFunction() + anotherThrowingFunction() // try applies to both function calls
sum = try (someThrowingFunction() + anotherThrowingFunction()) // try applies to both function calls
sum = (try someThrowingFunction()) + anotherThrowingFunction() // Error: try applies only to the first function call
一个try表达式不能出现在二元运算符的右侧,除非二元运算符是赋值运算符,或者try表达式被括在圆括号中。
有关更多信息和查看如何使用try的示例,请尝试?并尝试!,请参阅错误处理。
try-operator → try- try-? -try-!-
二进制表达式
二进制表达式将中缀二元运算符与它作为其左手和右手参数所使用的表达式结合在一起。 它有以下形式:
left-hand argumentoperatorright-hand argument
有关这些运算符行为的信息,请参阅基本运算符和高级运算符。
有关Swift标准库提供的运算符的信息,请参阅运算符声明。
注意
在解析时,由二元运算符组成的表达式表示为一个扁平列表。该列表通过应用运算符优先级转换为树。例如,表达式2 + 3 * 5最初被理解为包含5个项目的平坦列表,2,+,3,*和5.此过程将其转换为树(2 +(3 * 5))。
格雷马二元表达式
binary-expression→binary-operatorprefix-expression
binary-expression→assign-operatortry-operatoroptprefix-expression
binary-expression→conditional-operatortry-operatoroptprefix-expression
二进制表达式→类型转换运算符
binary-expressions→binary-expressionbinary-expressionsopt
作业操作员
赋值运算符为给定表达式设置一个新值。它有以下形式:
expression = value
表达式的值设置为通过评估值获得的值。 如果表达式是元组,则该值必须是具有相同元素数的元组。 (允许嵌套元组)。赋值是从值的每个部分执行到表达式的相应部分。 例如:
(a, _, (b, c)) = ("test", 9.45, (12, 3))
// a is "test", b is 12, c is 3, and 9.45 is ignored
赋值运算符不返回任何值
assignment-operator → =-
三元条件运算符
三元条件运算符根据条件值评估两个给定值中的一个。 它有以下形式:
condition ? expression used if true : expression used if false
如果条件评估为true,则条件运算符将评估第一个表达式并返回其值。 否则,它将评估第二个表达式并返回其值。 未使用的表达式未被评估。
有关使用三元条件运算符的示例,请参阅三元条件运算符
conditional-operator → ?-expression-:-
类型转换运算符
有四种类型转换运算符:is运算符,as运算符,as? 运算符和as!
他们有以下形式:
expressionistype
expressionastype
expressionas? type
expressionas! type
is运算符在运行时检查表达式是否可以转换为指定的类型。 如果表达式可以转换为指定的类型,则返回true; 否则,它返回false。
如果在编译时知道演员总是成功,as操作员会执行演员,如上传或桥接。 向上转换允许您使用表达式作为其类型的超类型的实例,而不使用中间变量。 以下方法是等效的:
func f(_any: Any) { print("Function for Any") }
func f(_int: Int) { print("Function for Int") }
let x = 10
f(x)
// Prints "Function for Int"
let y: Any = x
f(y)
// Prints "Function for Any"
f(xasAny)
// Prints "Function for Any"
桥接允许您使用Swift标准库类型(如String)的表达式作为其相应的基础类型(如NSString),而无需创建新实例。有关桥接的更多信息,请参阅在Cocoa和Objective-C中使用Swift使用可可数据类型(Swift 4.1)。
as?运算符将表达式的条件转换执行为指定的类型。 as?运算符返回指定类型的可选项。在运行时,如果转换成功,表达式的值被包装在一个可选的并返回;否则,返回的值为零。如果保证转换为指定类型失败或保证成功,则会引发编译时错误。
as!运算符将表达式强制转换为指定的类型。 as!运算符返回指定类型的值,而不是可选类型。如果转换失败,则会引发运行时错误。 x的行为! T与(x??T)!的行为相同。
有关类型转换以及查看使用类型转换运算符的示例的更多信息,请参阅类型转换
type-casting-operator → is-type-
type-casting-operator → as-type-
type-casting-operator → as-?-type-
type-casting-operator → as-!-type-
主要表达式
主要表现形式是最基本的表达形式。 它们可以单独用作表达式,并且可以将它们与其他标记进行组合以制作前缀表达式,二进制表达式和后缀表达式。
primary-expression → identifier-generic-argument-clause-opt-
primary-expression → literal-expression-
primary-expression → self-expression-
primary-expression → superclass-expression-
primary-expression → closure-expression-
primary-expression → parenthesized-expression-
primary-expression → tuple-expression-
primary-expression → implicit-member-expression-
primary-expression → wildcard-expression-
primary-expression → key-path-expression-
primary-expression → selector-expression-
primary-expression → key-path-string-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的值是该函数的名称,在方法中它是该方法的名称,在属性getter或setter中,它是该属性的名称,在init或subscript等特殊成员内部 该关键字的名称,在文件的顶层,它是当前模块的名称。
当用作函数或方法参数的默认值时,特殊文字的值在调用站点处计算默认值表达式时确定。
func logFunctionName(string: String = #function) {
print(string)
}
func myFunction() {
logFunctionName() // Prints "myFunction()".
}
数组文字是有序的值集合。 它有以下形式:
[value 1, value 2, ...]
数组中的最后一个表达式可以跟随一个可选的逗号。 数组文本的值有类型[T],其中T是其中的表达式的类型。 如果有多种类型的表达式,T是他们最接近的共同超类型。 空数组文字是使用一对空方括号写入的,可用于创建指定类型的空数组。
var emptyArray: [Double] = []
字典文字是键值对的无序集合。 它有以下形式:
[key 1: value 1, key 2: value 2, ...]
字典中的最后一个表达式可以跟随一个可选的逗号。 字典文字的值的类型是[Key:Value],其中Key是其关键表达式的类型,Value是其值表达式的类型。 如果有多种类型的表达式,则键和值是它们各自值最接近的常见超类型。 空字典文字在一对括号([:])内写成冒号,以将其与空数组文字区分开来。 您可以使用空字典文本来创建指定键和值类型的空字典文字。
varemptyDictionary: [String: Double] = [:]
Xcode使用操场文字在程序编辑器中创建颜色,文件或图像的交互式表示。 Xcode以外的纯文本中的游乐场文字使用特殊的文字语法表示。
有关在Xcode中使用操场文字的信息,请参阅Xcode帮助>使用操场>添加文字。
语法表达的语法
literal-expression → literal-
literal-expression → array-literal-dictionary-literal-playground-literal-
literal-expression → #file-#line-#column-#function-
array-literal → [-array-literal-items-opt-]-
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-
playground-literal → #colorLiteral-(-red-:-expression-,-green-:-expression-,-blue-:-expression-,-alpha-:-expression-)-
playground-literal → #fileLiteral-(-resourceName-:-expression-)-
playground-literal → #imageLiteral-(-resourceName-:-expression-)-
自我表达
自我表达式是对当前类型或其出现类型的实例的明确引用。 它有以下几种形式:
self
self.member name
self[subscript index]
self(initializer arguments)
self.init(initializer arguments)
在初始化器,下标或实例方法中,self指的是它所在类型的当前实例。 在类型方法中,self指的是它出现的当前类型。
self表达式用于指定访问成员时的作用域,当scope中存在另一个具有相同名称的变量(如函数参数)时提供消歧。 例如:
class SomeClass {
var greeting: String
init(greeting: String) {
self.greeting = greeting
}
}
在值类型的变异方法中,可以将该值类型的新实例指定给self。 例如:
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(xdeltaX: Double, ydeltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
self-expression → self-self-method-expression-self-subscript-expression-self-initializer-expression-
self-method-expression → self-.-identifier-
self-subscript-expression → self-[-function-call-argument-list-]-
self-initializer-expression → self-.-init-
超类表达
超类表达式允许类与其超类进行交互。 它有以下几种形式之一:
super.member name
super[subscript index]
super.init(initializer arguments)
第一种形式用于访问超类的成员。 第二种形式用于访问超类的下标实现。 第三种形式用于访问超类的初始化程序。
子类可以在其成员,下标和初始化程序的实现中使用超类表达式来使用其超类中的实现。
super class-expression → superclass-method-expression-superclass-subscript-expression-superclass-initializer-expression-
super class-method-expression → super-.-identifier-
super class-subscript-expression → super-[-function-call-argument-list-]-
super class-initializer-expression → super-.-init-
闭包表达式
闭包表达式创建闭包,也称为lambda或其他编程语言中的匿名函数。 就像一个函数声明一样,一个闭包含有声明,并且从它的封闭范围中捕获常量和变量。 它有以下形式:
{ (parameters) -> return typein
statements
}
参数与函数声明中的参数具有相同的形式,如函数声明中所述。
有几种特殊形式可以更简洁地书写闭包:
闭包可以省略其参数的类型,其返回类型或两者。 如果省略参数名称和两种类型,请在语句前省略in关键字。 如果无法推断省略的类型,则会引发编译时错误。
闭包可能会忽略其参数的名称。 然后它的参数隐式命名为$,后跟它们的位置:$ 0,$ 1,$ 2等等。
只包含单个表达式的闭包被理解为返回该表达式的值。 在对周围表达式执行类型推断时,也会考虑此表达式的内容。
以下封闭表达式是等效的:
myFunction { (x: Int, y: Int) -> Intin
return x + y
}
myFunction { x, yin
return x + y
}
myFunction { return$0 + $1 }
myFunction { $0 + $1 }
有关将闭包作为参数传递给函数的信息,请参阅函数调用表达式。
可以使用Closure表达式,而不必将其存储在变量或常量中,例如当您立即使用闭包作为函数调用的一部分时。在上面的代码中传递给myFunction的闭包表达式就是这种直接使用的例子。因此,闭包表达式是逃避还是不逃逸取决于表达式的周围环境。如果闭包表达式立即被调用,或者作为非逸出函数参数传递,则它是非空转的。否则,闭包表达式将被转义。
有关转义闭包的更多信息,请参阅转义闭包。
捕获列表
默认情况下,闭包表达式捕获周围范围的常量和变量,并强引用这些值。您可以使用捕获列表明确控制如何在闭包中捕获值。
在参数列表之前,捕获列表被写为逗号分隔的由方括号包围的表达式列表。如果使用捕获列表,即使省略参数名称,参数类型和返回类型,也必须使用in关键字。
捕获列表中的条目在创建闭包时被初始化。对于捕获列表中的每个条目,常量将初始化为周围范围中具有相同名称的常量或变量的值。例如,在下面的代码中,a包含在捕获列表中,但b不是,这使它们具有不同的行为。
vara = 0
varb = 0
let closure = { [a] in
print(a, b)
}
a = 10
b = 10
closure()
// Prints "0 10"
有两种不同的名称,即周围范围的变量和封闭范围中的常量,但只有一个名为b的变量。 内部作用域中的a在创建闭包时用外部作用域中的a的值进行初始化,但它们的值不以任何特殊方式连接。 这意味着,对外部范围中的a值的更改不会影响内部范围中的a的值,对闭合内部的更改也不会影响闭合外部的值。 相比之下,在外部范围中只有一个名为b-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()
// Prints "10 10"
如果表达式值的类型是类,则可以使用弱或无主的方式将表达式标记在捕获列表中,以捕获表达式值的弱引用或无主引用。
myFunction { print(self.title) } // strong capture
myFunction { [weakself] inprint(self!.title) } // weak capture
myFunction { [unownedself] inprint(self.title) } // unowned capture
您还可以将任意表达式绑定到捕获列表中的命名值。 在创建闭包时评估表达式,并使用指定的强度捕获值。 例如:
// Weak capture of "self.parent" as "parent"
myFunction { [weakparent = self.parent] inprint(parent!.title) }
有关闭包表达式的更多信息和示例,请参阅闭包表达式。 有关捕获列表的更多信息和示例,请参阅解决闭包的强参考周期。
GRAMMAR OF A CLOSURE EXPRESSION
closure-expression → {-closure-signature-opt-statements-opt-}-
closure-signature → capture-list-opt-closure-parameter-clause-throws-opt-function-result-opt-in-
closure-signature → capture-list-in-
closure-parameter-clause → (-)-(-closure-parameter-list-)-identifier-list-
closure-parameter-list → closure-parameter-closure-parameter-,-closure-parameter-list-
closure-parameter → closure-parameter-name-type-annotation-opt-
closure-parameter → closure-parameter-name-type-annotation-...-
closure-parameter-name → identifier-
capture-list → [-capture-list-items-]-
capture-list-items → capture-list-item-capture-list-item-,-capture-list-items-
capture-list-item → capture-specifier-opt-expression-
capture-specifier → weak-unowned-unowned(safe)-unowned(unsafe)-
隐含成员表达
隐式成员表达式是在类型推断可以确定隐含类型的上下文中访问类型成员(如枚举案例或类型方法)的缩写方式。 它有以下形式:
.member name
For example:
var x = MyEnumeration.someValue
x = .anotherValue
GRAMMAR OF A IMPLICIT MEMBER EXPRESSION
implicit-member-expression → .-identifier-
括号表达式
括号表达式由括号括起来的表达式组成。 您可以使用括号通过显式分组表达式来指定操作的优先级。 分组括号不会改变表达式的类型 - 例如,(1)的类型只是Int
GRAMMAR OF A PARENTHESIZED EXPRESSION
parenthesized-expression → (-expression-)-
元组表达
元组表达式由逗号分隔的由括号括起来的表达式列表组成。 每个表达式可以有一个可选的标识符,用冒号(:)分隔。 它有以下形式:
(identifier 1: expression 1, identifier 2: expression 2, …)
元组表达式可以包含零表达式,也可以包含两个或更多表达式。 圆括号内的单个表达式是一个加括号的表达式。
注意
在Swift中写入()一个空元组表达式和一个空元组类型。 因为Void是()的类型别名,所以你可以用它来写一个空的元组类型。 但是,像所有类型的别名一样,Void总是一个类型 - 你不能用它来写一个空的元组表达式。
tuple-expression → (-)-(-tuple-element-,-tuple-element-list-)-
tuple-element-list → tuple-element-tuple-element-,-tuple-element-list-
tuple-element → expression-identifier-:-expression-
通配符表达式
通配符表达式用于在赋值期间显式忽略值。 例如,在下面的赋值10中分配给x,而忽略20:
(x, _) = (10, 20)
// x is 10, and 20 is ignored
GRAMMAR OF A WILDCARD EXPRESSION
wildcard-expression → _-
关键路径表达
关键路径表达式是指类型的属性或下标。 您在动态编程任务中使用关键路径表达式,例如键值观察。 他们有以下形式:
\type name.path
类型名称是具体类型的名称,包括任何通用参数,如String,[Int]或Set <Int>。
该路径由属性名称,下标,可选链表达式和强制解包表达式组成。 这些关键路径组件中的每一个都可以根据需要按任意顺序重复多次。
在编译时,键路径表达式被KeyPath类的一个实例替代。
要使用键路径访问值,请将键路径传递给下标(keyPath :)下标,该下标在所有类型上都可用。 例如:
struct SomeStructure {
var someValue: Int
}
let s = SomeStructure(someValue: 12)
let pathToProperty = \SomeStructure.someValue
let value = s[keyPath: pathToProperty]
// value is 12
类型名称可以在类型推断可以确定隐含类型的上下文中省略。 以下代码使用\ .someProperty而不是\ SomeClass.someProperty:
class SomeClass: NSObject {
@objcvarsomeProperty: Int
init(someProperty: Int) {
self.someProperty = someProperty
}
}
let c = SomeClass(someProperty: 10)
c.observe(\.someProperty) { object, changein
// ...
}
该路径可以包含多个以句点分隔的属性名称,以引用属性值的属性。 此代码使用键路径表达式\ OuterStructure.outer.someValue来访问OuterStructure类型的外部属性的someValue属性:
struct OuterStructure {
var outer: SomeStructure
init(someValue: Int) {
self.outer = SomeStructure(someValue: someValue)
}
}
let nested = OuterStructure(someValue: 24)
let nestedKeyPath = \OuterStructure.outer.someValue
let nestedValue = nested[keyPath: nestedKeyPath]
// nestedValue is 24
只要下标的参数类型符合Hashable协议,路径就可以包含使用括号的下标。 本示例在关键路径中使用下标来访问数组的第二个元素:
let greetings = ["hello", "hola", "bonjour", "??"]
let myGreeting = greetings[keyPath: \[String].[1]]
// myGreeting is 'hola'
下标中使用的值可以是命名值或文字。 使用值语义在关键路径中捕获值。 以下代码在关键路径表达式和闭包中使用变量index来访问greetings数组的第三个元素。 当索引被修改时,关键路径表达式仍然引用第三个元素,而闭包使用新的索引。
var index = 2
let path = \[String].[index]
let fn: ([String]) -> String = { stringsinstrings[index] }
print(greetings[keyPath: path])
// Prints "bonjour"
print(fn(greetings))
// Prints "bonjour"
// Setting 'index' to a new value doesn't affect 'path'
index += 1
print(greetings[keyPath: path])
// Prints "bonjour"
// Because 'fn' closes over 'index', it uses the new value
print(fn(greetings))
// Prints "??"
路径可以使用可选的链接和强制解包。 此代码在关键路径中使用可选链接来访问可选字符串的属性:
let firstGreeting: String? = greetings.first
print(firstGreeting?.countasAny)
// Prints "Optional(5)"
// Do the same thing using a key path.
let count = greetings[keyPath: \[String].first?.count]
print(countasAny)
// Prints "Optional(5)"
您可以混合和匹配关键路径的组件,以访问深度嵌套在类型中的值。 以下代码通过使用组合这些组件的关键路径表达式来访问数组字典的不同值和属性。
let interestingNumbers = ["prime": [2, 3, 5, 7, 11, 13, 15],
"triangular": [1, 3, 6, 10, 15, 21, 28],
"hexagonal": [1, 6, 15, 28, 45, 66, 91]]
print(interestingNumbers[keyPath: \[String: [Int]].["prime"]] asAny)
// Prints "Optional([2, 3, 5, 7, 11, 13, 15])"
print(interestingNumbers[keyPath: \[String: [Int]].["prime"]![0]])
// Prints "2"
print(interestingNumbers[keyPath: \[String: [Int]].["hexagonal"]!.count])
// Prints "7"
print(interestingNumbers[keyPath: \[String: [Int]].["hexagonal"]!.count.bitWidth])
// Prints "64"
有关在与Objective-C API交互的代码中使用键路径的更多信息,请参阅使用Cocoa和Objective-C中的Swift中的键和键路径(Swift 4.1)。 有关键值编码和键值观测的信息,请参阅键值编码编程指南和键值观测编程指南
GRAMMAR OF A KEY-PATH EXPRESSION
key-path-expression → \-type-opt-.-key-path-components-
key-path-components → key-path-component-key-path-component-.-key-path-components-
key-path-component → identifier-key-path-postfixes-opt-key-path-postfixes-
key-path-postfixes → key-path-postfix-key-path-postfixes-opt-
key-path-postfix → ?-!-[-function-call-argument-list-]-
选择器表达式
选择器表达式允许您访问用于引用方法或Objective-C中属性的getter或setter的选择器。 它有以下形式:
#selector(method name)
#selector(getter: property name)
#selector(setter: property name)
方法名称和属性名称必须是对Objective-C运行时中可用的方法或属性的引用。 选择器表达式的值是Selector类型的一个实例。 例如:
class SomeClass: NSObject {
@objcletproperty: String
@objc(doSomethingWithInt:)
func doSomething(_x: Int) {}
init(property: String) {
self.property = property
}
}
let selectorForMethod = #selector(SomeClass.doSomething(_:))
let selectorForPropertyGetter = #selector(getter: SomeClass.property)
为属性的getter创建选择器时,属性名称可以是对变量或常量属性的引用。 相比之下,当为属性的setter创建选择器时,属性名称只能是对变量属性的引用。
方法名可以包含用于分组的括号,以及用于在共享名称但具有不同类型签名的方法之间消除歧义的as运算符。 例如:
extension SomeClass {
@objc(doSomethingWithString:)
func doSomething(_x: String) { }
}
let anotherSelector = #selector(SomeClass.doSomething(_:) as (SomeClass) -> (String) -> Void)
由于选择器是在编译时创建的,而不是在运行时创建的,因此编译器可以检查方法或属性是否存在以及它们是否暴露于Objective-C运行时。
注意
虽然方法名称和属性名称是表达式,但它们从不被评估。
有关在与Objective-C API交互的Swift代码中使用选择器的更多信息,请参阅使用Cocoa和Objective-C的Swift中的Objective-C选择器(Swift 4.1)。
selector-expression → #selector-(-expression-)-
selector-expression → #selector-(-getter:-expression-)-
selector-expression → #selector-(-setter:-expression-)-
关键路径字符串表达式
通过键路径字符串表达式,您可以访问用于引用Objective-C中属性的字符串,以用于键值编码和键值观测API。 它有以下形式:
#keyPath(property name)
属性名称必须是对Objective-C运行时中可用属性的引用。 在编译时,键路径字符串表达式被字符串文字替换。 例如:
class SomeClass: NSObject {
@objcvarsomeProperty: Int
init(someProperty: Int) {
self.someProperty = someProperty
}
}
let c = SomeClass(someProperty: 12)
let keyPath = #keyPath(SomeClass.someProperty)
if let value = c.value(forKey: keyPath) {
print(value)
}
// Prints "12"
在类中使用键路径字符串表达式时,可以通过只写属性名称来引用该类的属性,而不使用类名称。
extension SomeClass {
func getSomeKeyPath() -> String {
return #keyPath(someProperty)
}
}
print(keyPath == c.getSomeKeyPath())
// Prints "true"
因为关键路径字符串是在编译时创建的,而不是在运行时创建的,所以编译器可以检查该属性是否存在以及该属性是否暴露给Objective-C运行时。
有关在与Objective-C API交互的Swift代码中使用键路径的更多信息,请参阅使用Cocoa和Objective-C中的Swift中的键和键路径(Swift 4.1)。 有关键值编码和键值观测的信息,请参阅键值编码编程指南和键值观测编程指南。
注意
虽然属性名称是一个表达式,但它永远不会被评估。
key-path-string-expression → #keyPath-(-expression-)-
后缀表达式
后缀表达式是通过将后缀运算符或其他后缀语法应用于表达式而形成的。 在语法上,每个主表达式也是一个后缀表达式。
有关这些运算符行为的信息,请参阅基本运算符和高级运算符。
有关Swift标准库提供的运算符的信息,请参阅运算符声明。
postfix-expression → primary-expression-
postfix-expression → postfix-expression-postfix-operator-
postfix-expression → function-call-expression-
postfix-expression → initializer-expression-
postfix-expression → explicit-member-expression-
postfix-expression → postfix-self-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 takes an integer and a closure as its arguments
someFunction(x: x, f: {$0 == 13})
someFunction(x: x) {$0 == 13}
如果尾部闭包是函数的唯一参数,则可以省略括号。
// someMethod takes a closure as its only argument
myData.someMethod() {$0 == 13}
myData.someMethod {$0 == 13}
GRAMMAR OF A FUNCTION CALL EXPRESSION
function-call-expression → postfix-expression-function-call-argument-clause-
function-call-expression → postfix-expression-function-call-argument-clause-opt-trailing-closure-
function-call-argument-clause → (-)-(-function-call-argument-list-)-
function-call-argument-list → function-call-argument-function-call-argument-,-function-call-argument-list-
function-call-argument → expression-identifier-:-expression-
function-call-argument → operator-identifier-:-operator-
trailing-closure → closure-expression-
初始化表达式
初始化表达式提供对类型初始值设定项的访问。 它有以下形式:
expression.init(initializer arguments)
您可以在函数调用表达式中使用初始化表达式来初始化一个新类型的实例。 您还可以使用初始化表达式来委派给超类的初始化程序。
class SomeSubClass: SomeSuperClass {
override init() {
// subclass initialization goes here
super.init()
}
}
像函数一样,初始化器可以用作一个值。 例如:
// Type annotation is required because String has multiple initializers.
let initializer: (Int) -> String = String.init
let oneTwoThree = [1, 2, 3].map(initializer).reduce("", +)
print(oneTwoThree)
// Prints "123"
如果按名称指定类型,则可以访问该类型的初始值设定项,而不使用初始值设定项表达式。 在所有其他情况下,您必须使用初始化表达式。
lets1 = SomeType.init(data: 3) // Valid
lets2 = SomeType(data: 1) // Also valid
lets3 = type(of: someValue).init(data: 7) // Valid
lets4 = type(of: someValue)(data: 5) // Error
GRAMMAR OF AN INITIALIZER EXPRESSION
initializer-expression → postfix-expression-.-init-
initializer-expression → postfix-expression-.-init-(-argument-names-)-
显式成员表达
显式成员表达式允许访问指定类型,元组或模块的成员。 它由项目和其成员的标识符之间的句点(。)组成。
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
// Now t is (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// Ambiguous
let b = instance.someMethod(x:y:) // Unambiguous
let d = instance.overloadedMethod// Ambiguous
let d = instance.overloadedMethod(x:y:) // Still ambiguous
let d: (Int, Bool) -> Void = instance.overloadedMethod(x:y:) // Unambiguous
如果一个句点出现在一行的开头,则它被理解为显式成员表达式的一部分,而不是隐式成员表达式。 例如,下面的清单显示了分成几行的链式方法调用:
let x = [10, 3, 20, 15, 4]
.sorted()
.filter { $0 > 5 }
.map { $0 * 100 }
GRAMMAR OF AN EXPLICIT MEMBER EXPRESSION
explicit-member-expression → postfix-expression-.-decimal-digits-
explicit-member-expression → postfix-expression-.-identifier-generic-argument-clause-opt-
explicit-member-expression → postfix-expression-.-identifier-(-argument-names-)-
argument-names → argument-name-argument-names-opt-
argument-name → identifier-:-
Postfix自我表达
后缀自我表达由一个表达式或一个类型的名称组成,后面紧跟着.self。 它有以下几种形式:
expression.self
type.self
第一种形式评估表达式的值。 例如,x.self评估为x。
第二种形式评估为该类型的值。 使用此表单来访问某个类型的值。 例如,由于SomeClass.self自身评估为SomeClass类型,因此可以将它传递给接受类型级参数的函数或方法。
postfix-self-expression → postfix-expression-.-self-
下标表达
下标表达式使用相应下标声明的getter和setter提供下标访问。 它有以下形式:
expression[index expressions]
为了评估下标表达式的值,用表达式类型的下标getter作为下标参数传入索引表达式。 为了设置它的值,下标设置器以相同的方式被调用。
有关下标声明的信息,请参阅协议下标声明
subscript-expression → postfix-expression-[-function-call-argument-list-]-
强制值表达
强制值表达式展开一个可选值,您肯定不是零。 它有以下形式:
expression!
如果表达式的值不为零,则可选值将被解包并返回相应的非选项类型。 否则,会引发运行时错误。
可以修改强制值表达式的解包值,方法是对值本身进行变异,或者通过赋值给其中一个值的成员。 例如:
var x: Int? = 0
x! += 1
// x is now 1
var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]
someDictionary["a"]![0] = 100
// someDictionary is now ["b": [10, 20], "a": [100, 2, 3]]
GRAMMAR OF A FORCED-VALUE EXPRESSION
forced-value-expression → postfix-expression-!-
可选链表达式
可选链表达式提供了在后缀表达式中使用可选值的简化语法。 它有以下形式:
expression?
后缀?运算符在不改变表达式值的情况下从表达式中选择可选链表达式。
可选链表达式必须出现在后缀表达式中,它们使得后缀表达式以特殊方式进行评估。如果可选链表达式的值为零,则后缀表达式中的所有其他操作都将被忽略,整个后缀表达式的计算结果为零。如果可选链表达式的值不是零,则可选链表达式的值将被解包并用于计算其余的后缀表达式。无论哪种情况,后缀表达式的值仍然是可选类型。
如果包含可选链表达式的后缀表达式嵌套在其他后缀表达式中,则只有最外层表达式返回可选类型。在下面的例子中,当c不为零时,它的值被解包并用于评估.property,其值用于评估.performAction()。整个表达式c?.property.performAction()具有可选类型的值。
var c: SomeClass?
var result: Bool? = c?.property.performAction()
以下示例显示了上述示例的行为,而未使用可选链接。
var result: Bool?
if let unwrappedC = c {
result = unwrappedC.property.performAction()
}
可选链表达式的unwrapped值可以通过变更值本身或通过赋值给其中一个成员来修改。 如果可选链表达式的值为零,则不会评估赋值运算符右侧的表达式。 例如:
func someFunctionWithSideEffects() -> Int {
return42// No actual side effects.
}
var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]
someDictionary["not here"]?[0] = someFunctionWithSideEffects()
// someFunctionWithSideEffects is not evaluated
// someDictionary is still ["b": [10, 20], "a": [1, 2, 3]]
someDictionary["a"]?[0] = someFunctionWithSideEffects()
// someFunctionWithSideEffects is evaluated and returns 42
// someDictionary is now ["b": [10, 20], "a": [42, 2, 3]]
GRAMMAR OF AN OPTIONAL-CHAINING EXPRESSION
optional-chaining-expression → postfix-expression-?-
已有1位网友发表了看法:
xiyangw 评论于 [2022-12-28 19:05:58] 回复
swift 官方文档