Swift5.3 语言参考(四) 表达式
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/9740319.html
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
在Swift中,有四种表达式:前缀表达式,二进制表达式,主表达式和后缀表达式。评估表达式会返回一个值,导致副作用,或两者兼而有之。
前缀和二进制表达式允许您将运算符应用于较小的表达式。主要表达式在概念上是最简单的表达式,它们提供了一种访问值的方法。Postfix表达式(如前缀和二进制表达式)允许您使用后缀(例如函数调用和成员访问)构建更复杂的表达式。每种表达方式将在下面的部分中详细介绍。
1 GRAMMAR OF AN EXPRESSION 2 3 expression → try-operator opt prefix-expression binary-expressions opt 4 5 expression-list → expression | expression , expression-list
前缀表达式
前缀表达式将可选前缀运算符与表达式组合在一起。前缀运算符采用一个参数,即后面的表达式。
有关这些运算符的行为的信息,请参阅基本运算符和高级运算符。
有关Swift标准库提供的运算符的信息,请参阅运算符声明。
除了标准库操作符之外,您还可以&
在作为函数调用表达式的输入输出参数传递的变量名称之前使用。有关更多信息和查看示例,请参阅In-Out参数。
1 GRAMMAR OF A PREFIX EXPRESSION 2 3 prefix-expression → prefix-operator opt postfix-expression 4 5 prefix-expression → in-out-expression 6 7 in-out-expression → & identifier
try运算符
一个尝试表达由的try
操作之后,可以抛出一个错误的表达。它具有以下形式:
try expression
一个可选的试表达由的try?
操作之后,可以抛出一个错误的表达。它具有以下形式:
try? expression
如果表达不抛出一个错误,可选的试表达式的值是一个可选包含的价值表达。否则,optional-try表达式的值为nil
。
一个强制尝试表达由的try!
操作之后,可以抛出一个错误的表达。它具有以下形式:
try! expression
如果表达式抛出错误,则会产生运行时错误。
当在二元运算左手侧上的表达被标记try
,try?
或者try!
,该运算符适用于整个二进制表达式。也就是说,您可以使用括号来明确运算符的应用程序范围。
1 sum = try someThrowingFunction() + anotherThrowingFunction() // try applies to both function calls 2 sum = try (someThrowingFunction() + anotherThrowingFunction()) // try applies to both function calls 3 sum = (try someThrowingFunction()) + anotherThrowingFunction() // Error: try applies only to the first function call
try
表达式不能出现在二进制运算符的右手侧,除非二进制运算符是赋值运算符或try
表达被括号括起来。
欲了解更多信息,看看如何使用的例子try
,try?
和try!
,看到错误处理。
1 GRAMMAR OF A TRY EXPRESSION 2 3 try-operator → try | try ? | try !
二进制表达式
二进制表达式将中缀二元运算符与其左手和右手参数所使用的表达式组合在一起。它具有以下形式:
left-hand argument operator right-hand argument
有关这些运算符的行为的信息,请参阅基本运算符和高级运算符。
有关Swift标准库提供的运算符的信息,请参阅运算符声明。
注意
在分析时,由二元运算符组成的表达式表示为平面列表。通过应用运算符优先级将此列表转换为树。例如,表达最初理解为五个项目的平面列表,,,,,和。此过程将其转换为树(2 +(3 * 5))。2 + 3 * 5
2
+
3
*
5
1 GRAMMAR OF A BINARY EXPRESSION 2 3 binary-expression → binary-operator prefix-expression 4 5 binary-expression → assignment-operator try-operator opt prefix-expression 6 7 binary-expression → conditional-operator try-operator opt prefix-expression 8 9 binary-expression → type-casting-operator 10 11 binary-expressions → binary-expression binary-expressions opt
赋值运算符
该赋值运算符设置为特定表达的新值。它具有以下形式:
expression = value
所述的值表达被设置为通过评估所获得的值的值。如果表达式是元组,则该值必须是具有相同元素数的元组。(允许使用嵌套元组。)从值的每个部分到表达式的相应部分执行赋值。例如:
1 (a, _, (b, c)) = ("test", 9.45, (12, 3)) 2 // a is "test", b is 12, c is 3, and 9.45 is ignored
赋值运算符不返回任何值。
1 GRAMMAR OF AN ASSIGNMENT OPERATOR 2 3 assignment-operator → =
三元条件运算符
所述三元条件运算符评估为基于条件的值两个给定值中的一个。它具有以下形式:
condition ? expression used if true : expression used if false
如果条件求值为true
,则条件运算符计算第一个表达式并返回其值。否则,它将计算第二个表达式并返回其值。未评估未使用的表达式。
有关使用三元条件运算符的示例,请参阅三元条件运算符。
1 GRAMMAR OF A CONDITIONAL OPERATOR 2 3 conditional-operator → ? expression :
类型铸造运算符
有四种类型铸造运算符:is
运算符,as
运算符,as?
运算符和as!
运算符。
它们具有以下形式:
1 expression is type 2 expression as type 3 expression as? type 4 expression as! type
的is
在运行时运算符检查是否表达可以转换为指定的类型。true
如果表达式可以强制转换为指定的类型,则返回; 否则,它返回false
。
在as
当在该铸造总是成功,如向上转型或桥接编译时已知运算符进行的铸造。Upcasting允许您将表达式用作其类型的超类型的实例,而无需使用中间变量。以下方法是等效的:
1 func f(_ any: Any) { print("Function for Any") } 2 func f(_ int: Int) { print("Function for Int") } 3 let x = 10 4 f(x) 5 // Prints "Function for Int" 6 7 let y: Any = x 8 f(y) 9 // Prints "Function for Any" 10 11 f(x as Any) 12 // Prints "Function for Any"
桥接允许您使用Swift标准库类型的表达式,例如String
其相应的Foundation类型,例如NSString
无需创建新实例。有关桥接的更多信息,请参阅使用基础类型。
的as?
运算符执行的有条件铸造表达式为指定的类型。的as?
运算符返回指定的可选类型。在运行时,如果转换成功,表达式的值将包装在一个可选的并返回; 否则,返回的值是nil
。如果保证转换为指定类型失败或保证成功,则会引发编译时错误。
的as!
运算符执行的强制投表达为指定的类型。的as!
运算符返回指定的值类型,而不是一个可选类型。如果强制转换失败,则会引发运行时错误。行为与行为相同。x as! T
(x as? T)!
有关类型转换的更多信息以及查看使用类型转换运算符的示例,请参阅类型转换。
1 GRAMMAR OF A TYPE-CASTING OPERATOR 2 3 type-casting-operator → is type 4 5 type-casting-operator → as type 6 7 type-casting-operator → as ? type 8 9 type-casting-operator → as ! type
主要表达
主表达式是最基本的表达式。它们可以单独用作表达式,它们可以与其他标记组合以生成前缀表达式,二进制表达式和后缀表达式。
1 GRAMMAR OF A PRIMARY EXPRESSION 2 3 primary-expression → identifier generic-argument-clause opt 4 5 primary-expression → literal-expression 6 7 primary-expression → self-expression 8 9 primary-expression → superclass-expression 10 11 primary-expression → closure-expression 12 13 primary-expression → parenthesized-expression 14 15 primary-expression → tuple-expression 16 17 primary-expression → implicit-member-expression 18 19 primary-expression → wildcard-expression 20 21 primary-expression → key-path-expression 22 23 primary-expression → selector-expression 24 25 primary-expression → key-path-string-expression
文字表达
文字表达由任一普通的文字(如字符串或数字),阵列或字典文字,游乐场文字,或下列特殊文字中的一个:
文字 | 类型 | 值 |
---|---|---|
#file |
String |
它出现的文件的名称。 |
#line |
Int |
它出现的行号。 |
#column |
Int |
它开始的列号。 |
#function |
String |
它出现的声明的名称。 |
#dsohandle |
UnsafeRawPointer |
DSO(动态共享对象)处理在它出现的地方使用。 |
在函数内部,值#function
是该函数的名称,在方法中它是该方法的名称,在属性getter或setter中它是该属性的名称,在特殊成员内部init
或者subscript
是它的名称关键字,在文件的顶层,它是当前模块的名称。
当用作函数或方法参数的默认值时,在调用站点计算默认值表达式时,将确定特殊文字的值。
1 func logFunctionName(string: String = #function) { 2 print(string) 3 } 4 func myFunction() { 5 logFunctionName() // Prints "myFunction()". 6 }
一个字面数组是值的有序集合。它具有以下形式:
[value 1, value 2, ...]
数组中的最后一个表达式后面可以跟一个可选的逗号。数组文字的值具有类型[T]
,其中T
是表达式的类型。如果有多种类型的表达式,T
则是它们最接近的常见超类型。空数组文字使用一对空方括号编写,可用于创建指定类型的空数组。
var emptyArray: [Double] = []
一个字典文字是键值对的无序集合。它具有以下形式:
[key 1: value 1, key 2: value 2, ...]
字典中的最后一个表达式后面跟一个可选的逗号。字典文字的值具有类型,其中是键表达式的类型,并且是其值表达式的类型。如果存在多个类型的表达式,并且是它们各自值的最接近的公共超类型。空字典文字在一对方括号()内写为冒号,以区别于空数组文字。您可以使用空字典文字来创建指定键和值类型的空字典文字。[Key: Value]
Key
Value
Key
Value
[:]
var emptyDictionary: [String: Double] = [:]
游乐场字面所使用的Xcode创建颜色,文件或图像程序编辑器内的交互式表示。Xcode之外的纯文本中的游乐场文字使用特殊的文字语法表示。
有关在Xcode中使用playground文字的信息,请参阅在Xcode帮助中添加颜色,文件或图像文字。
1 GRAMMAR OF A LITERAL EXPRESSION 2 3 literal-expression → literal 4 5 literal-expression → array-literal | dictionary-literal | playground-literal 6 7 literal-expression → #file | #line | #column | #function | #dsohandle 8 9 array-literal → [ array-literal-items opt ] 10 11 array-literal-items → array-literal-item ,opt | array-literal-item , array-literal-items 12 13 array-literal-item → expression 14 15 dictionary-literal → [ dictionary-literal-items ] | [ : ] 16 17 dictionary-literal-items → dictionary-literal-item ,opt | dictionary-literal-item , dictionary-literal-items 18 19 dictionary-literal-item → expression : expression 20 21 playground-literal → #colorLiteral ( red : expression , green : expression , blue : expression , alpha : expression ) 22 23 playground-literal → #fileLiteral ( resourceName : expression ) 24 25 playground-literal → #imageLiteral ( resourceName : expression )
Self表达
self
表达是显式引用在其发生的类型的当前类型或实例。它有以下形式:
1 self 2 self.member name 3 self[subscript index] 4 self(initializer arguments) 5 self.init(initializer arguments)
在初始化程序,下标或实例方法中,self
引用它出现的类型的当前实例。在类型方法中,self
指的是它出现的当前类型。
的self
表达被用来访问成员时,提供消歧当在范围相同名称的另一变量,诸如函数的参数来指定范围。例如:
1 class SomeClass { 2 var greeting: String 3 init(greeting: String) { 4 self.greeting = greeting 5 } 6 }
在值类型的变异方法中,您可以为该值类型分配新实例self
。例如:
1 struct Point { 2 var x = 0.0, y = 0.0 3 mutating func moveBy(x deltaX: Double, y deltaY: Double) { 4 self = Point(x: x + deltaX, y: y + deltaY) 5 } 6 }
1 GRAMMAR OF A SELF EXPRESSION 2 3 self-expression → self | self-method-expression | self-subscript-expression | self-initializer-expression 4 5 self-method-expression → self . identifier 6 7 self-subscript-expression → self [ function-call-argument-list ] 8 9 self-initializer-expression → self . init
超类表达
一个超表达可以用其超一类交互。它具有以下形式之一:
1 super.member name 2 super[subscript index] 3 super.init(initializer arguments)
第一种形式用于访问超类的成员。第二种形式用于访问超类的下标实现。第三种形式用于访问超类的初始化程序。
子类可以在其成员,下标和初始化器的实现中使用超类表达式来使用其超类中的实现。
1 GRAMMAR OF A SUPERCLASS EXPRESSION 2 3 superclass-expression → superclass-method-expression | superclass-subscript-expression | superclass-initializer-expression 4 5 superclass-method-expression → super . identifier 6 7 superclass-subscript-expression → super [ function-call-argument-list ] 8 9 superclass-initializer-expression → super . init
闭合表达式
闭合表达式创建一个闭合,也被称为拉姆达或匿名函数在其他编程语言。与函数声明一样,闭包包含语句,它从其封闭范围捕获常量和变量。它具有以下形式:
1 { (parameters) -> return type in 2 statements 3 }
的参数具有相同的形式,如一个函数声明的参数,如描述函数声明。
有几种特殊形式可以更简洁地编写闭包:
- 闭包可以省略其参数的类型,返回类型或两者。如果省略参数名称和两种类型,请
in
在语句之前省略关键字。如果无法推断省略的类型,则会引发编译时错误。 - 闭包可以省略其参数的名称。它的参数,然后隐式命名
$
,然后他们的立场:$0
,$1
,$2
,等等。 - 仅包含单个表达式的闭包被理解为返回该表达式的值。在对周围表达式执行类型推断时,还会考虑此表达式的内容。
以下闭包表达式是等效的:
1 myFunction { (x: Int, y: Int) -> Int in 2 return x + y 3 } 4 5 myFunction { x, y in 6 return x + y 7 } 8 9 myFunction { return $0 + $1 } 10 11 myFunction { $0 + $1 }
有关将闭包作为参数传递给函数的信息,请参阅函数调用表达式。
闭包表达式可以在不存储在变量或常量中的情况下使用,例如当您立即使用闭包作为函数调用的一部分时。传递给myFunction
上面代码的闭包表达式就是这种直接使用的例子。因此,闭包表达式是转义还是非转义取决于表达式的周围上下文。如果立即调用闭包表达式或作为非脱节函数参数传递,则闭包表达式是非脱节表达式。否则,闭包表达式正在转义。
有关转义闭包的详细信息,请参阅转义闭包。
捕获列表
默认情况下,闭包表达式通过对这些值的强引用从其周围范围捕获常量和变量。您可以使用捕获列表显式控制在闭包中捕获值的方式。
捕获列表在参数列表之前写为以方括号括起的表达式的逗号分隔列表。如果使用捕获列表,则还必须使用in
关键字,即使省略参数名称,参数类型和返回类型也是如此。
创建闭包时,将初始化捕获列表中的条目。对于捕获列表中的每个条目,常量初始化为在周围范围内具有相同名称的常量或变量的值。例如,在下面的代码中,a
包含在捕获列表中但b
不包含,这给了它们不同的行为。
1 var a = 0 2 var b = 0 3 let closure = { [a] in 4 print(a, b) 5 } 6 7 a = 10 8 b = 10 9 closure() 10 // Prints "0 10"
命名有两个不同的东西a
,周围范围中的变量和闭包范围中的常量,但只有一个变量名为b
。的a
在内侧范围被初始化的值a
在当创建所述闭合外范围,但是它们的值不以任何特殊的方式连接。这意味着更改a
外部作用域中的值不会影响a
内部作用域中a
的值,闭包内部的更改也不会影响a
闭包外部的值。相比之下,在外部范围中只有一个名为b
-the的变量- b
因此在闭包内部或外部的变化在两个地方都可见。
当捕获的变量的类型具有引用语义时,此区别不可见。例如,x
下面的代码中有两个命名,外部作用域中的变量和内部作用域中的常量,但由于引用语义,它们都引用同一个对象。
1 class SimpleClass { 2 var value: Int = 0 3 } 4 var x = SimpleClass() 5 var y = SimpleClass() 6 let closure = { [x] in 7 print(x.value, y.value) 8 } 9 10 x.value = 10 11 y.value = 10 12 closure() 13 // Prints "10 10"
如果表达式的值的类型是类,则可以使用weak
或标记捕获列表中的表达式,以捕获unowned
对表达式值的弱或无主引用。
1 myFunction { print(self.title) } // implicit strong capture 2 myFunction { [self] in print(self.title) } // explicit strong capture 3 myFunction { [weak self] in print(self!.title) } // weak capture 4 myFunction { [unowned self] in print(self.title) } // unowned 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 3 closure-expression → { closure-signature opt statements opt } 4 5 closure-signature → capture-list opt closure-parameter-clause throwsopt function-result opt in 6 7 closure-signature → capture-list in 8 9 closure-parameter-clause → ( ) | ( closure-parameter-list ) | identifier-list 10 11 closure-parameter-list → closure-parameter | closure-parameter , closure-parameter-list 12 13 closure-parameter → closure-parameter-name type-annotation opt 14 15 closure-parameter → closure-parameter-name type-annotation ... 16 17 closure-parameter-name → identifier 18 19 capture-list → [ capture-list-items ] 20 21 capture-list-items → capture-list-item | capture-list-item , capture-list-items 22 23 capture-list-item → capture-specifier opt expression 24 25 capture-specifier → weak | unowned | unowned(safe) | unowned(unsafe)
隐式构件表达式
隐式构件表达式是访问类型的成员,例如枚举的情况下或类型的方法,在一个上下文,其中类型推理可确定隐含的类型的缩写方式。它具有以下形式:
.member name
例如:
1 var x = MyEnumeration.someValue 2 x = .anotherValue
1 GRAMMAR OF A IMPLICIT MEMBER EXPRESSION 2 3 implicit-member-expression → . identifier
括号表达式
带括号的表达式由括号括起的表达式组成。您可以使用括号通过显式分组表达式来指定操作的优先级。分组括号不会更改表达式的类型 - 例如,类型(1)
简单Int
。
1 GRAMMAR OF A PARENTHESIZED EXPRESSION 2 3 parenthesized-expression → ( expression )
元组表达
甲元组表达由逗号分隔用括号括的表达式列表的。每个表达式前面都有一个可选的标识符,用冒号(:
)分隔。它具有以下形式:
(identifier 1: expression 1, identifier 2: expression 2, ...)
元组表达式可以包含零个表达式,也可以包含两个或多个表达式。括号内的单个表达式是带括号的表达式。
注意
空元组表达式和空元组类型都是用()
Swift 编写的。因为Void
是类型别名()
,您可以使用它来编写空元组类型。但是,像所有类型别名一样,Void
它总是一个类型 - 你不能用它来写一个空的元组表达式。
1 GRAMMAR OF A TUPLE EXPRESSION 2 3 tuple-expression → ( ) | ( tuple-element , tuple-element-list ) 4 5 tuple-element-list → tuple-element | tuple-element , tuple-element-list 6 7 tuple-element → expression | identifier : expression
通配符表达
甲通配符表达式用于显式地忽略的分配期间的值。例如,在以下分配中分配10 x
,忽略20:
1 (x, _) = (10, 20) 2 // x is 10, and 20 is ignored
1 GRAMMAR OF A WILDCARD EXPRESSION 2 3 wildcard-expression → _
关键路径表达
关键路径表达是指一种类型的属性或下标。您可以在动态编程任务中使用键路径表达式,例如键值观察。它们具有以下形式:
\type name.path
的类型名称是一个具体类型,包括任何通用的参数,如名称String
,[Int]
或Set<Int>
。
该路径由属性名称,下标,可选链接表达式和强制解包表达式组成。这些关键路径组件中的每一个可以根据需要以任何顺序重复多次。
在编译时,键路径表达式被KeyPath
类的实例替换。
要使用密钥路径访问值,请将密钥路径传递给subscript(keyPath:)
下标,该下标适用于所有类型。例如:
1 struct SomeStructure { 2 var someValue: Int 3 } 4 5 let s = SomeStructure(someValue: 12) 6 let pathToProperty = \SomeStructure.someValue 7 8 let value = s[keyPath: pathToProperty] 9 // value is 12
该类型名称可以在上下文地方类型推断可以确定隐含的类型被省略。以下代码使用\.someProperty
而不是\SomeClass.someProperty
:
1 class SomeClass: NSObject { 2 @objc var someProperty: Int 3 init(someProperty: Int) { 4 self.someProperty = someProperty 5 } 6 } 7 8 let c = SomeClass(someProperty: 10) 9 c.observe(\.someProperty) { object, change in 10 // ... 11 }
该路径可以包含多个属性名称,用句点分隔,指属性值的属性。此代码使用的关键路径表达式\OuterStructure.outer.someValue
来访问someValue
该物业OuterStructure
类型的outer
属性:
1 struct OuterStructure { 2 var outer: SomeStructure 3 init(someValue: Int) { 4 self.outer = SomeStructure(someValue: someValue) 5 } 6 } 7 8 let nested = OuterStructure(someValue: 24) 9 let nestedKeyPath = \OuterStructure.outer.someValue 10 11 let nestedValue = nested[keyPath: nestedKeyPath] 12 // nestedValue is 24
所述路径可以使用支架,只要标的参数类型符合包括标Hashable
协议。此示例使用键路径中的下标来访问数组的第二个元素:
1 let greetings = ["hello", "hola", "bonjour", "안녕"] 2 let myGreeting = greetings[keyPath: \[String].[1]] 3 // myGreeting is 'hola'
下标中使用的值可以是命名值或文字。使用值语义在关键路径中捕获值。以下代码index
在键路径表达式和闭包中使用该变量来访问greetings
数组的第三个元素。当index
被修改时,关键路径表达式仍然引用第三元件,而封闭件使用新的索引。
1 var index = 2 2 let path = \[String].[index] 3 let fn: ([String]) -> String = { strings in strings[index] } 4 5 print(greetings[keyPath: path]) 6 // Prints "bonjour" 7 print(fn(greetings)) 8 // Prints "bonjour" 9 10 // Setting 'index' to a new value doesn't affect 'path' 11 index += 1 12 print(greetings[keyPath: path]) 13 // Prints "bonjour" 14 15 // Because 'fn' closes over 'index', it uses the new value 16 print(fn(greetings)) 17 // Prints "안녕"
该路径可以使用可选链接和强制解包。此代码在密钥路径中使用可选链接来访问可选字符串的属性:
1 let firstGreeting: String? = greetings.first 2 print(firstGreeting?.count as Any) 3 // Prints "Optional(5)" 4 5 // Do the same thing using a key path. 6 let count = greetings[keyPath: \[String].first?.count] 7 print(count as Any) 8 // Prints "Optional(5)"
您可以混合和匹配关键路径的组件,以访问深度嵌套在类型中的值。以下代码通过使用组合这些组件的键路径表达式访问数组字典的不同值和属性。
1 let interestingNumbers = ["prime": [2, 3, 5, 7, 11, 13, 17], 2 "triangular": [1, 3, 6, 10, 15, 21, 28], 3 "hexagonal": [1, 6, 15, 28, 45, 66, 91]] 4 print(interestingNumbers[keyPath: \[String: [Int]].["prime"]] as Any) 5 // Prints "Optional([2, 3, 5, 7, 11, 13, 17])" 6 print(interestingNumbers[keyPath: \[String: [Int]].["prime"]![0]]) 7 // Prints "2" 8 print(interestingNumbers[keyPath: \[String: [Int]].["hexagonal"]!.count]) 9 // Prints "7" 10 print(interestingNumbers[keyPath: \[String: [Int]].["hexagonal"]!.count.bitWidth]) 11 // Prints "64"
有关在与Objective-C API交互的代码中使用键路径的更多信息,请参阅在Swift中使用Objective-C运行时功能。有关键值编码和键值观察的信息,请参阅键值编码编程指南和键值观察编程指南。
1 GRAMMAR OF A KEY-PATH EXPRESSION 2 3 key-path-expression → \ type opt . key-path-components 4 5 key-path-components → key-path-component | key-path-component . key-path-components 6 7 key-path-component → identifier key-path-postfixes opt | key-path-postfixes 8 9 key-path-postfixes → key-path-postfix key-path-postfixes opt 10 11 key-path-postfix → ? | ! | [ function-call-argument-list ]
选择器表达式
选择器表达式允许您访问用于在Objective-C中引用方法或属性的getter或setter的选择器。它具有以下形式:
1 #selector(method name) 2 #selector(getter: property name) 3 #selector(setter: property name)
的方法名称和属性名称必须是一个方法或在Objective-C运行可用属性的引用。选择器表达式的值是该Selector
类型的实例。例如:
1 class SomeClass: NSObject { 2 @objc let property: String 3 @objc(doSomethingWithInt:) 4 func doSomething(_ x: Int) {} 5 6 init(property: String) { 7 self.property = property 8 } 9 } 10 let selectorForMethod = #selector(SomeClass.doSomething(_:)) 11 let selectorForPropertyGetter = #selector(getter: SomeClass.property)
为属性的getter创建选择器时,属性名称可以是对变量或常量属性的引用。相反,在为属性的setter创建选择器时,属性名称必须仅是对变量属性的引用。
该方法的名称可以包含括号进行分组,以及与as
运算符共享一个名称,但具有不同类型签名的方法之间的歧义。例如:
1 extension SomeClass { 2 @objc(doSomethingWithString:) 3 func doSomething(_ x: String) { } 4 } 5 let anotherSelector = #selector(SomeClass.doSomething(_:) as (SomeClass) -> (String) -> Void)
因为选择器是在编译时创建的,而不是在运行时创建的,所以编译器可以检查方法或属性是否存在以及它们是否暴露给Objective-C运行时。
注意
虽然方法名称和属性名称是表达式,但它们永远不会被评估。
有关在与Objective-C API交互的Swift代码中使用选择器的更多信息,请参阅在Swift中使用Objective-C运行时功能。
1 GRAMMAR OF A SELECTOR EXPRESSION 2 3 selector-expression → #selector ( expression ) 4 5 selector-expression → #selector ( getter: expression ) 6 7 selector-expression → #selector ( setter: expression )
键路径字符串表达式
通过键路径字符串表达式,您可以访问用于引用Objective-C中的属性的字符串,以用于键值编码和键值观察API。它具有以下形式:
#keyPath(property name)
该属性名称必须是是在Objective-C运行提供一个属性的参考。在编译时,键路径字符串表达式由字符串文字替换。例如:
1 class SomeClass: NSObject { 2 @objc var someProperty: Int 3 init(someProperty: Int) { 4 self.someProperty = someProperty 5 } 6 } 7 8 let c = SomeClass(someProperty: 12) 9 let keyPath = #keyPath(SomeClass.someProperty) 10 11 if let value = c.value(forKey: keyPath) { 12 print(value) 13 } 14 // Prints "12"
在类中使用键路径字符串表达式时,可以通过仅写入属性名称来引用该类的属性,而不使用类名。
1 extension SomeClass { 2 func getSomeKeyPath() -> String { 3 return #keyPath(someProperty) 4 } 5 } 6 print(keyPath == c.getSomeKeyPath()) 7 // Prints "true"
因为密钥路径字符串是在编译时创建的,而不是在运行时创建的,所以编译器可以检查该属性是否存在以及该属性是否暴露给Objective-C运行时。
有关在与Objective-C API交互的Swift代码中使用键路径的更多信息,请参阅在Swift中使用Objective-C运行时功能。有关键值编码和键值观察的信息,请参阅键值编码编程指南和键值观察编程指南。
注意
虽然属性名称是表达式,但永远不会对其进行求值。
1 GRAMMAR OF A KEY-PATH STRING EXPRESSION 2 3 key-path-string-expression → #keyPath ( expression )
后缀表达式
通过将后缀运算符或其他后缀语法应用于表达式来形成Postfix表达式。从语法上讲,每个主表达式也是一个后缀表达式。
有关这些运算符的行为的信息,请参阅基本运算符和高级运算符。
有关Swift标准库提供的运算符的信息,请参阅运算符声明。
1 GRAMMAR OF A POSTFIX EXPRESSION 2 3 postfix-expression → primary-expression 4 5 postfix-expression → postfix-expression postfix-operator 6 7 postfix-expression → function-call-expression 8 9 postfix-expression → initializer-expression 10 11 postfix-expression → explicit-member-expression 12 13 postfix-expression → postfix-self-expression 14 15 postfix-expression → subscript-expression 16 17 postfix-expression → forced-value-expression 18 19 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 takes an integer and a closure as its arguments 2 someFunction(x: x, f: {$0 == 13}) 3 someFunction(x: x) {$0 == 13}
如果尾部闭包是函数的唯一参数,则可以省略括号。
1 // someMethod takes a closure as its only argument 2 myData.someMethod() {$0 == 13} 3 myData.someMethod {$0 == 13}
1 GRAMMAR OF A FUNCTION CALL EXPRESSION 2 3 function-call-expression → postfix-expression function-call-argument-clause 4 5 function-call-expression → postfix-expression function-call-argument-clause opt trailing-closure 6 7 function-call-argument-clause → ( ) | ( function-call-argument-list ) 8 9 function-call-argument-list → function-call-argument | function-call-argument , function-call-argument-list 10 11 function-call-argument → expression | identifier : expression 12 13 function-call-argument → operator | identifier : operator 14 15 trailing-closure → closure-expression
初始化表达式
一个初始化表达式提供访问类型的初始化。它具有以下形式:
expression.init(initializer arguments)
您可以在函数调用表达式中使用初始化表达式来初始化类型的新实例。您还可以使用初始化表达式来委托超类的初始化程序。
1 class SomeSubClass: SomeSuperClass { 2 override init() { 3 // subclass initialization goes here 4 super.init() 5 } 6 }
像函数一样,初始值设定项可以用作值。例如:
1 // Type annotation is required because String has multiple initializers. 2 let initializer: (Int) -> String = String.init 3 let oneTwoThree = [1, 2, 3].map(initializer).reduce("", +) 4 print(oneTwoThree) 5 // Prints "123"
如果按名称指定类型,则可以在不使用初始值设定项表达式的情况下访问类型的初始值设定项。在所有其他情况下,您必须使用初始化表达式。
1 let s1 = SomeType.init(data: 3) // Valid 2 let s2 = SomeType(data: 1) // Also valid 3 4 let s3 = type(of: someValue).init(data: 7) // Valid 5 let s4 = type(of: someValue)(data: 5) // Error
1 GRAMMAR OF AN INITIALIZER EXPRESSION 2 3 initializer-expression → postfix-expression . init 4 5 initializer-expression → postfix-expression . init ( argument-names )
明确的成员表达
一个明确的成员表达式允许访问命名类型,一个元组,或模块的成员。它由.
项目和其成员的标识符之间的句点(.)组成。
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 // Now t is (20, 20, 30)
模块的成员访问该模块的顶级声明。
使用该dynamicMemberLookup
属性声明的类型包括在运行时查找的成员,如Attributes中所述。
要区分名称仅由其参数名称不同的方法或初始值设定项,请在括号中包含参数名称,每个参数名称后跟冒号(:
)。_
为没有名称的参数写下划线()。要区分重载方法,请使用类型注释。例如:
1 class SomeClass { 2 func someMethod(x: Int, y: Int) {} 3 func someMethod(x: Int, z: Int) {} 4 func overloadedMethod(x: Int, y: Int) {} 5 func overloadedMethod(x: Int, y: Bool) {} 6 } 7 let instance = SomeClass() 8 9 let a = instance.someMethod // Ambiguous 10 let b = instance.someMethod(x:y:) // Unambiguous 11 12 let d = instance.overloadedMethod // Ambiguous 13 let d = instance.overloadedMethod(x:y:) // Still ambiguous 14 let d: (Int, Bool) -> Void = instance.overloadedMethod(x:y:) // Unambiguous
如果句点出现在行的开头,则将其理解为显式成员表达式的一部分,而不是隐式成员表达式。例如,以下清单显示了分成几行的链式方法调用:
1 let x = [10, 3, 20, 15, 4] 2 .sorted() 3 .filter { $0 > 5 } 4 .map { $0 * 100 }
1 GRAMMAR OF AN EXPLICIT MEMBER EXPRESSION 2 3 explicit-member-expression → postfix-expression . decimal-digits 4 5 explicit-member-expression → postfix-expression . identifier generic-argument-clause opt 6 7 explicit-member-expression → postfix-expression . identifier ( argument-names ) 8 9 argument-names → argument-name argument-names opt 10 11 argument-name → identifier :
后缀Self表达
后缀self
表达式由表达式或类型名称组成,紧接着后跟.self
。它有以下形式:
1 expression.self 2 type.self
第一个表单计算表达式的值。例如,x.self
评估为x
。
第二种形式评估类型的值。使用此表单可以将类型作为值进行访问。例如,因为SomeClass.self
计算SomeClass
类型本身,您可以将其传递给接受类型级参数的函数或方法。
1 GRAMMAR OF A SELF EXPRESSION 2 3 postfix-self-expression → postfix-expression . self
下标表达
甲标表达式提供了使用吸气剂和相应的下标声明的设定器标访问。它具有以下形式:
expression[index expressions]
要评估下标表达式的值,将使用作为下标参数传递的索引表达式调用表达式类型的下标getter 。要设置其值,以相同的方式调用下标setter。
有关下标声明的信息,请参阅协议下标声明。
1 GRAMMAR OF A SUBSCRIPT EXPRESSION 2 3 subscript-expression → postfix-expression [ function-call-argument-list ]
强制价值表达
一个强制值表达式解开一个可选值,你一定不是nil
。它具有以下形式:
expression!
如果表达式的值不是nil
,则打开可选值并使用相应的非可选类型返回。否则,将引发运行时错误。
强制值表达式的展开值可以通过改变值本身或通过赋值给其中一个成员来修改。例如:
1 var x: Int? = 0 2 x! += 1 3 // x is now 1 4 5 var someDictionary = ["a": [1, 2, 3], "b": [10, 20]] 6 someDictionary["a"]![0] = 100 7 // someDictionary is now ["a": [100, 2, 3], "b": [10, 20]]
1 GRAMMAR OF A FORCED-VALUE EXPRESSION 2 3 forced-value-expression → postfix-expression !
可选链接表达式
一个可选的链式表达提供了一种用于在后缀表达式使用可选值的简化语法。它具有以下形式:
expression?
后缀运算符?从表达式生成可选链式表达式,而不更改表达式的值。
可选链接表达式必须出现在后缀表达式中,并且它们会导致以特殊方式计算后缀表达式。如果是optional-chaining表达式的值nil
,则忽略后缀表达式中的所有其他操作,并且整个后缀表达式的计算结果为nil
。如果optional-chaining表达式nil
的值不是,则可以解包可选链接表达式的值,并用于计算后缀表达式的其余部分。在任何一种情况下,后缀表达式的值仍然是可选类型。
如果包含可选链接表达式的后缀表达式嵌套在其他后缀表达式中,则只有最外层表达式返回可选类型。在下面的示例中,如果c
不是nil
,则将其值展开并用于评估.property
,其值用于评估.performAction()
。整个表达式c?.property.performAction()
具有可选类型的值。
1 var c: SomeClass? 2 var result: Bool? = c?.property.performAction()
以下示例显示了上述示例的行为,但未使用可选链接。
1 var result: Bool? 2 if let unwrappedC = c { 3 result = unwrappedC.property.performAction() 4 }
可以通过改变值本身或通过赋值给其中一个成员来修改可选链接表达式的展开值。如果optional-chaining表达式的值为nil
,则不评估赋值运算符右侧的表达式。例如:
1 func someFunctionWithSideEffects() -> Int { 2 return 42 // No actual side effects. 3 } 4 var someDictionary = ["a": [1, 2, 3], "b": [10, 20]] 5 6 someDictionary["not here"]?[0] = someFunctionWithSideEffects() 7 // someFunctionWithSideEffects is not evaluated 8 // someDictionary is still ["a": [1, 2, 3], "b": [10, 20]] 9 10 someDictionary["a"]?[0] = someFunctionWithSideEffects() 11 // someFunctionWithSideEffects is evaluated and returns 42 12 // someDictionary is now ["a": [42, 2, 3], "b": [10, 20]]
1 GRAMMAR OF AN OPTIONAL-CHAINING EXPRESSION 2 3 optional-chaining-expression → postfix-expression ?