這里的教程為Swift官方教程中文版。

表達(dá)式(Expressions)


1.0 翻譯:sg552 校對(duì):numbbbbb, stanzhai

2.0 翻譯+校對(duì):EudeMorgen

2.1 翻譯:mmoaay

2.2 校對(duì):175

3.0 翻譯+校對(duì):chenmingjia

本頁包含內(nèi)容:

Swift 中存在四種表達(dá)式:前綴表達(dá)式,二元表達(dá)式,基本表達(dá)式和后綴表達(dá)式。表達(dá)式在返回一個(gè)值的同時(shí)還可以引發(fā)副作用。

通過前綴表達(dá)式和二元表達(dá)式可以對(duì)簡單表達(dá)式使用各種運(yùn)算符?;颈磉_(dá)式從概念上講是最簡單的一種表達(dá)式,它是一種訪問值的方式。后綴表達(dá)式則允許你建立復(fù)雜的表達(dá)式,例如函數(shù)調(diào)用和成員訪問。每種表達(dá)式都在下面有詳細(xì)論述。

表達(dá)式語法
</a> 表達(dá)式try運(yùn)算符可選 前綴表達(dá)式 二元表達(dá)式列表可選
表達(dá)式列表表達(dá)式 | 表達(dá)式 , 表達(dá)式列表

前綴表達(dá)式

前綴表達(dá)式由可選的前綴運(yùn)算符和表達(dá)式組成。前綴運(yùn)算符只接收一個(gè)參數(shù),表達(dá)式則緊隨其后。

關(guān)于這些運(yùn)算符的更多信息,請參閱 基本運(yùn)算符高級(jí)運(yùn)算符。

關(guān)于 Swift 標(biāo)準(zhǔn)庫提供的運(yùn)算符的更多信息,請參閱 Swift Standard Library Operators Reference。

除了標(biāo)準(zhǔn)庫運(yùn)算符,你也可以對(duì)某個(gè)變量使用 & 運(yùn)算符,從而將其傳遞給函數(shù)的輸入輸出參數(shù)。更多信息,請參閱 輸入輸出參數(shù)。

前綴表達(dá)式語法
</a> 前綴表達(dá)式前綴運(yùn)算符可選 后綴表達(dá)式
前綴表達(dá)式輸入輸出表達(dá)式
輸入輸出表達(dá)式& 標(biāo)識(shí)符

try 運(yùn)算符

try 表達(dá)式由 try 運(yùn)算符加上緊隨其后的可拋出錯(cuò)誤的表達(dá)式組成,形式如下:

try 可拋出錯(cuò)誤的表達(dá)式

可選的 try 表達(dá)式由 try? 運(yùn)算符加上緊隨其后的可拋出錯(cuò)誤的表達(dá)式組成,形式如下:

try? 可拋出錯(cuò)誤的表達(dá)式

如果可拋出錯(cuò)誤的表達(dá)式?jīng)]有拋出錯(cuò)誤,整個(gè)表達(dá)式返回的可選值將包含可拋出錯(cuò)誤的表達(dá)式的返回值,否則,該可選值為 nil。

強(qiáng)制的 try 表達(dá)式由 try! 運(yùn)算符加上緊隨其后的可拋出錯(cuò)誤的表達(dá)式組成,形式如下:

try! 可拋出錯(cuò)誤的表達(dá)式

如果可拋出錯(cuò)誤的表達(dá)式拋出了錯(cuò)誤,將會(huì)引發(fā)運(yùn)行時(shí)錯(cuò)誤。

在二進(jìn)制運(yùn)算符左側(cè)的表達(dá)式被標(biāo)記上 try、try? 或者 try! 時(shí),這個(gè)運(yùn)算符對(duì)整個(gè)二進(jìn)制表達(dá)式都產(chǎn)生作用。也就是說,你可以使用括號(hào)來明確運(yùn)算符的作用范圍。

sum = try someThrowingFunction() + anotherThrowingFunction()   // try 對(duì)兩個(gè)函數(shù)調(diào)用都產(chǎn)生作用
sum = try (someThrowingFunction() + anotherThrowingFunction()) // try 對(duì)兩個(gè)函數(shù)調(diào)用都產(chǎn)生作用
sum = (try someThrowingFunction()) + anotherThrowingFunction() // 錯(cuò)誤:try 只對(duì)第一個(gè)函數(shù)調(diào)用產(chǎn)生作用

try 表達(dá)式不能出現(xiàn)在二進(jìn)制運(yùn)算符的的右側(cè),除非二進(jìn)制運(yùn)算符是賦值運(yùn)算符或者 try 表達(dá)式是被圓括號(hào)括起來的。

關(guān)于 try、try?try! 的更多信息,以及該如何使用的例子,請參閱 錯(cuò)誤處理。

try 表達(dá)式語法
try 運(yùn)算符try | try? | try!

二元表達(dá)式

二元表達(dá)式由中綴運(yùn)算符和左右參數(shù)表達(dá)式組成。形式如下:

左側(cè)參數(shù) 二元運(yùn)算符 右側(cè)參數(shù)

關(guān)于這些運(yùn)算符的更多信息,請參閱 基本運(yùn)算符高級(jí)運(yùn)算符

關(guān)于 Swift 標(biāo)準(zhǔn)庫提供的運(yùn)算符的更多信息,請參閱 Swift Standard Library Operators Reference

注意
在解析時(shí),一個(gè)二元表達(dá)式將作為一個(gè)扁平列表表示,然后根據(jù)運(yùn)算符的優(yōu)先級(jí),再進(jìn)一步進(jìn)行組合。例如,2 + 3 * 5 首先被看作具有五個(gè)元素的列表,即 2、+、3*、5,隨后根據(jù)運(yùn)算符優(yōu)先級(jí)組合為 (2 + (3 * 5))

二元表達(dá)式語法
二元表達(dá)式二元運(yùn)算符 前綴表達(dá)式
二元表達(dá)式賦值運(yùn)算符 try運(yùn)算符可選 前綴表達(dá)式
二元表達(dá)式條件運(yùn)算符 try運(yùn)算符可選 前綴表達(dá)式
二元表達(dá)式類型轉(zhuǎn)換運(yùn)算符
二元表達(dá)式列表二元表達(dá)式 二元表達(dá)式列表可選

賦值表達(dá)式

賦值表達(dá)式會(huì)為某個(gè)給定的表達(dá)式賦值,形式如下;

表達(dá)式 =

右邊的值會(huì)被賦值給左邊的表達(dá)式。如果左邊表達(dá)式是一個(gè)元組,那么右邊必須是一個(gè)具有同樣元素個(gè)數(shù)的元組。(嵌套元組也是允許的。)右邊的值中的每一部分都會(huì)被賦值給左邊的表達(dá)式中的相應(yīng)部分。例如:

(a, _, (b, c)) = ("test", 9.45, (12, 3))
// a 為 "test",b 為 12,c 為 3,9.45 會(huì)被忽略

賦值運(yùn)算符不返回任何值。

賦值運(yùn)算符語法
賦值運(yùn)算符=

三元條件運(yùn)算符

三元條件運(yùn)算符會(huì)根據(jù)條件來對(duì)兩個(gè)給定表達(dá)式中的一個(gè)進(jìn)行求值,形式如下:

條件 ? 表達(dá)式(條件為真則使用) : 表達(dá)式(條件為假則使用)

如果條件為真,那么對(duì)第一個(gè)表達(dá)式進(jìn)行求值并返回結(jié)果。否則,對(duì)第二個(gè)表達(dá)式進(jìn)行求值并返回結(jié)果。未使用的表達(dá)式不會(huì)進(jìn)行求值。

關(guān)于使用三元條件運(yùn)算符的例子,請參閱 三元條件運(yùn)算符

三元條件運(yùn)算符語法
三元條件運(yùn)算符? try運(yùn)算符可選 表達(dá)式 :

類型轉(zhuǎn)換運(yùn)算符

有 4 種類型轉(zhuǎn)換運(yùn)算符:is、as、as?as!。它們有如下的形式:

表達(dá)式 is 類型
表達(dá)式 as 類型
表達(dá)式 as? 類型
表達(dá)式 as! 類型

is 運(yùn)算符在運(yùn)行時(shí)檢查表達(dá)式能否向下轉(zhuǎn)化為指定的類型,如果可以則返回 ture,否則返回 false。

as 運(yùn)算符在編譯時(shí)執(zhí)行向上轉(zhuǎn)換和橋接。向上轉(zhuǎn)換可將表達(dá)式轉(zhuǎn)換成超類的實(shí)例而無需使用任何中間變量。以下表達(dá)式是等價(jià)的:

func f(any: Any) { print("Function for Any") }
func f(int: Int) { print("Function for Int") }
let x = 10
f(x)
// 打印 “Function for Int”

let y: Any = x
f(y)
// 打印 “Function for Any”

f(x as Any)
// 打印 “Function for Any”

橋接可將 Swift 標(biāo)準(zhǔn)庫中的類型(例如 String)作為一個(gè)與之相關(guān)的 Foundation 類型(例如 NSString)來使用,而不需要新建一個(gè)實(shí)例。關(guān)于橋接的更多信息,請參閱 Using Swift with Cocoa and Objective-C (Swift2.2) 中的 Working with Cocoa Data Types。

as? 運(yùn)算符有條件地執(zhí)行類型轉(zhuǎn)換,返回目標(biāo)類型的可選值。在運(yùn)行時(shí),如果轉(zhuǎn)換成功,返回的可選值將包含轉(zhuǎn)換后的值,否則返回 nil。如果在編譯時(shí)就能確定轉(zhuǎn)換一定會(huì)成功或是失敗,則會(huì)導(dǎo)致編譯報(bào)錯(cuò)。

as! 運(yùn)算符執(zhí)行強(qiáng)制類型轉(zhuǎn)換,返回目標(biāo)類型的非可選值。如果轉(zhuǎn)換失敗,則會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。表達(dá)式 x as! T 效果等同于 (x as? T)!。

關(guān)于類型轉(zhuǎn)換的更多內(nèi)容和例子,請參閱 類型轉(zhuǎn)換

類型轉(zhuǎn)換運(yùn)算符語法
類型轉(zhuǎn)換運(yùn)算符is 類型
類型轉(zhuǎn)換運(yùn)算符as 類型
類型轉(zhuǎn)換運(yùn)算符as ? 類型
類型轉(zhuǎn)換運(yùn)算符as ! 類型

基本表達(dá)式

基本表達(dá)式是最基本的表達(dá)式。它們可以單獨(dú)使用,也可以跟前綴表達(dá)式、二元表達(dá)式、后綴表達(dá)式組合使用。

基本表達(dá)式語法
基本表達(dá)式標(biāo)識(shí)符 泛型實(shí)參子句可選
基本表達(dá)式字面量表達(dá)式
基本表達(dá)式self表達(dá)式
基本表達(dá)式超類表達(dá)式
基本表達(dá)式閉包表達(dá)式
基本表達(dá)式圓括號(hào)表達(dá)式
基本表達(dá)式隱式成員表達(dá)式
基本表達(dá)式通配符表達(dá)式
基本表達(dá)式選擇器表達(dá)式

字面量表達(dá)式

字面量表達(dá)式可由普通字面量(例如字符串或者數(shù)字),字典或者數(shù)組字面量,或者下面列表中的特殊字面量組成:

字面量 類型
#file String 所在的文件名
#line Int 所在的行數(shù)
#column Int 所在的列數(shù)
#function String 所在的聲明的名字

#line除了上述含義外,還有另一種含義。當(dāng)它出現(xiàn)在單獨(dú)一行時(shí),會(huì)被理解成行控制語句,請參閱線路控制語句。

對(duì)于 #function,在函數(shù)中會(huì)返回當(dāng)前函數(shù)的名字,在方法中會(huì)返回當(dāng)前方法的名字,在屬性的存取器中會(huì)返回屬性的名字,在特殊的成員如 initsubscript 中會(huì)返回這個(gè)關(guān)鍵字的名字,在某個(gè)文件中會(huì)返回當(dāng)前模塊的名字。

#function 作為函數(shù)或者方法的默認(rèn)參數(shù)值時(shí),該字面量的值取決于函數(shù)或方法的調(diào)用環(huán)境。

func logFunctionName(string: String = #function) {
    print(string)
}
func myFunction() {
    logFunctionName() 
}
myFunction() // 打印 “myFunction()”

數(shù)組字面量是值的有序集合,形式如下:

[值 1, 值 2, ...]

數(shù)組中的最后一個(gè)表達(dá)式可以緊跟一個(gè)逗號(hào)。數(shù)組字面量的類型是 [T],這個(gè) T 就是數(shù)組中元素的類型。如果數(shù)組中包含多種類型,T 則是跟這些類型最近的的公共父類型??諗?shù)組字面量由一組方括號(hào)定義,可用來創(chuàng)建特定類型的空數(shù)組。

var emptyArray: [Double] = []

字典字面量是一個(gè)包含無序鍵值對(duì)的集合,形式如下:

[鍵 1 : 值 1, 鍵 2 : 值 2, ...]

字典中的最后一個(gè)表達(dá)式可以緊跟一個(gè)逗號(hào)。字典字面量的類型是 [Key : Value]Key 表示鍵的類型,Value 表示值的類型。如果字典中包含多種類型,那么 Key 表示的類型則為所有鍵最接近的公共父類型,Value 與之相似。一個(gè)空的字典字面量由方括號(hào)中加一個(gè)冒號(hào)組成([:]),從而與空數(shù)組字面量區(qū)分開,可以使用空字典字面量來創(chuàng)建特定類型的字典。

var emptyDictionary: [String : Double] = [:]

字面量表達(dá)式語法

字面量表達(dá)式字面量
字面量表達(dá)式數(shù)組字面量 | 字典字面量
字面量表達(dá)式#file | #line | #column | #function

數(shù)組字面量[ 數(shù)組字面量項(xiàng)列表可選 ]
</a> 數(shù)組字面量項(xiàng)列表數(shù)組字面量項(xiàng) ,可選 | 數(shù)組字面量項(xiàng) , 數(shù)組字面量項(xiàng)列表
數(shù)組字面量項(xiàng)表達(dá)式

字典字面量[ 字典字面量項(xiàng)列表 ] | [ : ]
</a> 字典字面量項(xiàng)列表字典字面量項(xiàng) ,可選 | 字典字面量項(xiàng) , 字典字面量項(xiàng)列表
字典字面量項(xiàng)表達(dá)式 : 表達(dá)式

self 表達(dá)式

self 表達(dá)式是對(duì)當(dāng)前類型或者當(dāng)前實(shí)例的顯式引用,它有如下形式:

self
self.成員名稱
self[下標(biāo)索引]
self(構(gòu)造器參數(shù))
self.init(構(gòu)造器參數(shù))

如果在構(gòu)造器、下標(biāo)、實(shí)例方法中,self 引用的是當(dāng)前類型的實(shí)例。在一個(gè)類型方法中,self 引用的是當(dāng)前的類型。

當(dāng)訪問成員時(shí),self 可用來區(qū)分重名變量,例如函數(shù)的參數(shù):

class SomeClass {
    var greeting: String
    init(greeting: String) {
        self.greeting = greeting
    }
}

mutating 方法中,你可以對(duì) 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)
    }
}

self 表達(dá)式語法
self 表達(dá)式self | self 方法表達(dá)式self 下標(biāo)表達(dá)式 | self 構(gòu)造器表達(dá)式

</a> self 方法表達(dá)式self . 標(biāo)識(shí)符
self 下標(biāo)表達(dá)式self [ 表達(dá)式 ] self 構(gòu)造器表達(dá)式self . init

超類表達(dá)式

超類表達(dá)式可以使我們在某個(gè)類中訪問它的超類,它有如下形式:

super.成員名稱
super[下標(biāo)索引]
super.init(構(gòu)造器參數(shù))

第一種形式用來訪問超類的某個(gè)成員,第二種形式用來訪問超類的下標(biāo),第三種形式用來訪問超類的構(gòu)造器。

子類可以通過超類表達(dá)式在它們的成員、下標(biāo)和構(gòu)造器中使用超類中的實(shí)現(xiàn)。

超類表達(dá)式語法
</a> 超類表達(dá)式超類方法表達(dá)式 | 超類下標(biāo)表達(dá)式 | 超類構(gòu)造器表達(dá)式
超類方法表達(dá)式super . 標(biāo)識(shí)符
</a> 超類下標(biāo)表達(dá)式super [ 表達(dá)式 ]
超類構(gòu)造器表達(dá)式super . init

閉包表達(dá)式

閉包表達(dá)式會(huì)創(chuàng)建一個(gè)閉包,在其他語言中也叫 lambda 或匿名函數(shù)。跟函數(shù)一樣,閉包包含了待執(zhí)行的代碼,不同的是閉包還會(huì)捕獲所在環(huán)境中的常量和變量。它的形式如下:

{ (parameters) -> return type in  
    statements  
}

閉包的參數(shù)聲明形式跟函數(shù)一樣,請參閱 函數(shù)聲明

閉包還有幾種特殊的形式,能讓閉包使用起來更加簡潔:

  • 閉包可以省略它的參數(shù)和返回值的類型。如果省略了參數(shù)名和所有的類型,也要省略 in 關(guān)鍵字。如果被省略的類型無法被編譯器推斷,那么就會(huì)導(dǎo)致編譯錯(cuò)誤。
  • 閉包可以省略參數(shù)名,參數(shù)會(huì)被隱式命名為 $ 加上其索引位置,例如 $0、$1、$2 分別表示第一個(gè)、第二個(gè)、第三個(gè)參數(shù),以此類推。
  • 如果閉包中只包含一個(gè)表達(dá)式,那么該表達(dá)式的結(jié)果就會(huì)被視為閉包的返回值。表達(dá)式結(jié)果的類型也會(huì)被推斷為閉包的返回類型。

下面幾個(gè)閉包表達(dá)式是等價(jià)的:

myFunction {
    (x: Int, y: Int) -> Int in
    return x + y
}

myFunction {
    (x, y) in
    return x + y
}

myFunction { return $0 + $1 }

myFunction { $0 + $1 }

關(guān)于如何將閉包作為參數(shù)來傳遞的內(nèi)容,請參閱 函數(shù)調(diào)用表達(dá)式。

捕獲列表

默認(rèn)情況下,閉包會(huì)捕獲附近作用域中的常量和變量,并使用強(qiáng)引用指向它們。你可以通過一個(gè)捕獲列表來顯式指定它的捕獲行為。

捕獲列表在參數(shù)列表之前,由中括號(hào)括起來,里面是由逗號(hào)分隔的一系列表達(dá)式。一旦使用了捕獲列表,就必須使用 in 關(guān)鍵字,即使省略了參數(shù)名、參數(shù)類型和返回類型。

捕獲列表中的項(xiàng)會(huì)在閉包創(chuàng)建時(shí)被初始化。每一項(xiàng)都會(huì)用閉包附近作用域中的同名常量或者變量的值初始化。例如下面的代碼示例中,捕獲列表包含 a 而不包含 b,這將導(dǎo)致這兩個(gè)變量具有不同的行為。

var a = 0
var b = 0
let closure = { [a] in
    print(a, b)
}

a = 10
b = 10
closure()
// 打印 “0 10”

在示例中,變量 b 只有一個(gè),然而,變量 a 有兩個(gè),一個(gè)在閉包外,一個(gè)在閉包內(nèi)。閉包內(nèi)的變量 a 會(huì)在閉包創(chuàng)建時(shí)用閉包外的變量 a 的值來初始化,除此之外它們并無其他聯(lián)系。這意味著在閉包創(chuàng)建后,改變某個(gè) a 的值都不會(huì)對(duì)另一個(gè) a 的值造成任何影響。與此相反,閉包內(nèi)外都是同一個(gè)變量 b,因此在閉包外改變其值,閉包內(nèi)的值也會(huì)受影響。

如果閉包捕獲的值具有引用語義則有所不同。例如,下面示例中,有兩個(gè)變量 x,一個(gè)在閉包外,一個(gè)在閉包內(nèi),由于它們的值是引用語義,雖然這是兩個(gè)不同的變量,它們卻都引用著同一實(shí)例。

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()
// 打印 “10 10”

如果捕獲列表中的值是類類型,你可以使用 weak 或者 unowned 來修飾它,閉包會(huì)分別用弱引用和無主引用來捕獲該值。

myFunction { print(self.title) }                   // 以強(qiáng)引用捕獲
myFunction { [weak self] in print(self!.title) }   // 以弱引用捕獲
myFunction { [unowned self] in print(self.title) } // 以無主引用捕獲

在捕獲列表中,也可以將任意表達(dá)式的值綁定到一個(gè)常量上。該表達(dá)式會(huì)在閉包被創(chuàng)建時(shí)進(jìn)行求值,閉包會(huì)按照指定的引用類型來捕獲表達(dá)式的值。例如:

// 以弱引用捕獲 self.parent 并賦值給 parent
myFunction { [weak parent = self.parent] in print(parent!.title) }

關(guān)于閉包表達(dá)式的更多信息和例子,請參閱 閉包表達(dá)式。關(guān)于捕獲列表的更多信息和例子,請參閱 解決閉包引起的循環(huán)強(qiáng)引用。

閉包表達(dá)式語法

閉包表達(dá)式{ 閉包簽名可選 語句 }

閉包簽名參數(shù)子句 函數(shù)結(jié)果可選 in
閉包簽名標(biāo)識(shí)符列表 函數(shù)結(jié)果可選 in
閉包簽名捕獲列表 參數(shù)子句 函數(shù)結(jié)果可選 in
閉包簽名捕獲列表 標(biāo)識(shí)符列表 函數(shù)結(jié)果可選 in
閉包簽名捕獲列表 in

捕獲列表[ 捕獲列表項(xiàng)列表 ]
</a> 捕獲列表項(xiàng)列表捕獲列表項(xiàng) | 捕獲列表項(xiàng) , 捕獲列表項(xiàng)列表
捕獲列表項(xiàng)捕獲說明符可選 表達(dá)式
捕獲說明符weak | unowned | unowned(safe) | unowned(unsafe)

隱式成員表達(dá)式

若類型可被推斷出來,可以使用隱式成員表達(dá)式來訪問某個(gè)類型的成員(例如某個(gè)枚舉成員或某個(gè)類型方法),形式如下:

.成員名稱

例如:

var x = MyEnumeration.SomeValue
x = .AnotherValue

隱式成員表達(dá)式語法
隱式成員表達(dá)式. 標(biāo)識(shí)符

圓括號(hào)表達(dá)式

圓括號(hào)表達(dá)式由圓括號(hào)和其中多個(gè)逗號(hào)分隔的子表達(dá)式組成。每個(gè)子表達(dá)式前面可以有一個(gè)標(biāo)識(shí)符,用冒號(hào)隔開。圓括號(hào)表達(dá)式形式如下:

(標(biāo)識(shí)符 1 : 表達(dá)式 1, 標(biāo)識(shí)符 2 : 表達(dá)式 2, ...)

使用圓括號(hào)表達(dá)式來創(chuàng)建元組,然后將其作為參數(shù)傳遞給函數(shù)。如果某個(gè)圓括號(hào)表達(dá)式中只有一個(gè)子表達(dá)式,那么它的類型就是子表達(dá)式的類型。例如,表達(dá)式 (1) 的類型是 Int,而不是 (Int)。

圓括號(hào)表達(dá)式語法
</a> 圓括號(hào)表達(dá)式( 表達(dá)式元素列表可選 )
表達(dá)式元素列表表達(dá)式元素 | 表達(dá)式元素 , 表達(dá)式元素列表
表達(dá)式元素表達(dá)式 | 標(biāo)識(shí)符 : 表達(dá)式

通配符表達(dá)式

通配符表達(dá)式可以在賦值過程中顯式忽略某個(gè)值。例如下面的代碼中,10 被賦值給 x,而 20 則被忽略:

(x, _) = (10, 20)
// x 為 10,20 被忽略

通配符表達(dá)式語法
通配符表達(dá)式_

選擇器表達(dá)式

選擇器表達(dá)式可以讓你通過選擇器來引用在Objective-C中方法(method)和屬性(property)的setter和getter方法。

#selector(方法名) #selector(getter: 屬性名) #selector(setter: 屬性名)

方法名和屬性名必須是存在于 Objective-C 運(yùn)行時(shí)中的方法和屬性的引用。選擇器表達(dá)式的返回值是一個(gè) Selector 類型的實(shí)例。例如:

class SomeClass: NSObject {
    let property: String
    @objc(doSomethingWithInt:)
    func doSomething(_ x: Int) { }

    init(property: String) {
        self.property = property
    }
}
let selectorForMethod = #selector(SomeClass.doSomething(_:))
let selectorForPropertyGetter = #selector(getter: SomeClass.property)

當(dāng)為屬性的getter創(chuàng)建選擇器時(shí),屬性名可以是變量屬性或者常量屬性的引用。但是當(dāng)為屬性的setter創(chuàng)建選擇器時(shí),屬性名只可以是對(duì)變量屬性的引用。

方法名稱可以包含圓括號(hào)來進(jìn)行分組,并使用as 操作符來區(qū)分具有相同方法名但類型不同的方法, 例如:

extension SomeClass {
    @objc(doSomethingWithString:)
    func doSomething(_ x: String) { }
}
let anotherSelector = #selector(SomeClass.doSomething(_:) as (SomeClass) -> (String) -> Void)

由于選擇器是在編譯時(shí)創(chuàng)建的,因此編譯器可以檢查方法或者屬性是否存在,以及是否在運(yùn)行時(shí)暴露給了 Objective-C 。

注意
雖然方法名或者屬性名是個(gè)表達(dá)式,但是它不會(huì)被求值。

更多關(guān)于如何在 Swift 代碼中使用選擇器來與 Objective-C API 進(jìn)行交互的信息,請參閱 Using Swift with Cocoa and Objective-C (Swift 3)Objective-C Selectors部分。

選擇器表達(dá)式語法
選擇器表達(dá)式#selector ( 表達(dá)式 )
選擇器表達(dá)式#selector ( getter:表達(dá)式 )
選擇器表達(dá)式#selector ( setter:表達(dá)式 )

后綴表達(dá)式

后綴表達(dá)式就是在某個(gè)表達(dá)式的后面運(yùn)用后綴運(yùn)算符或其他后綴語法。從語法構(gòu)成上來看,基本表達(dá)式也是后綴表達(dá)式。

關(guān)于這些運(yùn)算符的更多信息,請參閱 基本運(yùn)算符高級(jí)運(yùn)算符。

關(guān)于 Swift 標(biāo)準(zhǔn)庫提供的運(yùn)算符的更多信息,請參閱 Swift Standard Library Operators Reference。

后綴表達(dá)式語法
后綴表達(dá)式基本表達(dá)式
后綴表達(dá)式后綴表達(dá)式 后綴運(yùn)算符
后綴表達(dá)式函數(shù)調(diào)用表達(dá)式
后綴表達(dá)式構(gòu)造器表達(dá)式
后綴表達(dá)式顯式成員表達(dá)式
后綴表達(dá)式后綴 self 表達(dá)式
后綴表達(dá)式dynamicType 表達(dá)式
后綴表達(dá)式下標(biāo)表達(dá)式
后綴表達(dá)式強(qiáng)制取值表達(dá)式
后綴表達(dá)式可選鏈表達(dá)式

函數(shù)調(diào)用表達(dá)式

函數(shù)調(diào)用表達(dá)式由函數(shù)名和參數(shù)列表組成,形式如下:

函數(shù)名(參數(shù) 1, 參數(shù) 2)

函數(shù)名可以是值為函數(shù)類型的任意表達(dá)式。

如果函數(shù)聲明中指定了參數(shù)的名字,那么在調(diào)用的時(shí)候也必須得寫出來。這種函數(shù)調(diào)用表達(dá)式具有以下形式:

函數(shù)名(參數(shù)名 1: 參數(shù) 1, 參數(shù)名 2: 參數(shù) 2)

如果函數(shù)的最后一個(gè)參數(shù)是函數(shù)類型,可以在函數(shù)調(diào)用表達(dá)式的尾部(右圓括號(hào)之后)加上一個(gè)閉包,該閉包會(huì)作為函數(shù)的最后一個(gè)參數(shù)。如下兩種寫法是等價(jià)的:

// someFunction 接受整數(shù)和閉包參數(shù)
someFunction(x, f: {$0 == 13})
someFunction(x) {$0 == 13}

如果閉包是該函數(shù)的唯一參數(shù),那么圓括號(hào)可以省略。

// someFunction 只接受一個(gè)閉包參數(shù)
myData.someMethod() {$0 == 13}
myData.someMethod {$0 == 13}

函數(shù)調(diào)用表達(dá)式語法
</a> 函數(shù)調(diào)用表達(dá)式后綴表達(dá)式 圓括號(hào)表達(dá)式
函數(shù)調(diào)用表達(dá)式后綴表達(dá)式 圓括號(hào)表達(dá)式可選 尾隨閉包
尾隨閉包閉包表達(dá)式

構(gòu)造器表達(dá)式

構(gòu)造器表達(dá)式用于訪問某個(gè)類型的構(gòu)造器,形式如下:

表達(dá)式.init(構(gòu)造器參數(shù))

你可以在函數(shù)調(diào)用表達(dá)式中使用構(gòu)造器表達(dá)式來初始化某個(gè)類型的新實(shí)例。也可以使用構(gòu)造器表達(dá)式來代理給超類構(gòu)造器。

class SomeSubClass: SomeSuperClass {
    override init() {
        // 此處為子類構(gòu)造過程
        super.init()
    }
}

和函數(shù)類似,構(gòu)造器表達(dá)式可以作為一個(gè)值。 例如:

// 類型注解是必須的,因?yàn)?String 類型有多種構(gòu)造器
let initializer: Int -> String = String.init
let oneTwoThree = [1, 2, 3].map(initializer).reduce("", combine: +)
print(oneTwoThree)
// 打印 “123”

如果通過名字來指定某個(gè)類型,可以不用構(gòu)造器表達(dá)式而直接使用類型的構(gòu)造器。在其他情況下,你必須使用構(gòu)造器表達(dá)式。

let s1 = SomeType.init(data: 3) // 有效
let s2 = SomeType(data: 1)      // 有效

let s4 = someValue.dynamicType(data: 5)      // 錯(cuò)誤
let s3 = someValue.dynamicType.init(data: 7) // 有效

構(gòu)造器表達(dá)式語法
構(gòu)造器表達(dá)式后綴表達(dá)式 . init
構(gòu)造器表達(dá)式后綴表達(dá)式 . init ( 參數(shù)名稱 )

顯式成員表達(dá)式

顯式成員表達(dá)式允許我們訪問命名類型、元組或者模塊的成員,其形式如下:

表達(dá)式.成員名

命名類型的某個(gè)成員在原始實(shí)現(xiàn)或者擴(kuò)展中定義,例如:

class SomeClass {
    var someProperty = 42
}
let c = SomeClass()
let y = c.someProperty // 訪問成員

元組的成員會(huì)隱式地根據(jù)表示它們出現(xiàn)順序的整數(shù)來命名,以 0 開始,例如:

var t = (10, 20, 30)
t.0 = t.1
// 現(xiàn)在元組 t 為 (20, 20, 30)

對(duì)于模塊的成員來說,只能直接訪問頂級(jí)聲明中的成員。

為了區(qū)分只有參數(shù)名有所不同的方法或構(gòu)造器,在圓括號(hào)中寫出參數(shù)名,參數(shù)名后緊跟一個(gè)冒號(hào),對(duì)于沒有參數(shù)名的參數(shù),使用下劃線代替參數(shù)名。而對(duì)于重載方法,則需使用類型標(biāo)注進(jìn)行區(qū)分。例如:

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              // 有歧義
let b = instance.someMethod(_:y:)        // 無歧義

let d = instance.overloadedMethod        // 有歧義
let d = instance.overloadedMethod(_:y:)  // 有歧義
let d: (Int, Bool) -> Void  = instance.overloadedMethod(_:y:)  // 無歧義

如果點(diǎn)號(hào)(.)出現(xiàn)在行首,它會(huì)被視為顯式成員表達(dá)式的一部分,而不是隱式成員表達(dá)式的一部分。例如如下代碼所展示的被分為多行的鏈?zhǔn)椒椒ㄕ{(diào)用:

let x = [10, 3, 20, 15, 4]
    .sort()
    .filter { $0 > 5 }
    .map { $0 * 100 }

顯式成員表達(dá)式語法
顯式成員表達(dá)式后綴表達(dá)式 . 十進(jìn)制數(shù)字
顯式成員表達(dá)式后綴表達(dá)式 . 標(biāo)識(shí)符 泛型實(shí)參子句可選
顯式成員表達(dá)式后綴表達(dá)式 . 標(biāo)識(shí)符 ( 參數(shù)名稱 )

</a> 參數(shù)名稱參數(shù)名 參數(shù)名稱可選
參數(shù)名標(biāo)識(shí)符 :

后綴 self 表達(dá)式

后綴 self 表達(dá)式由某個(gè)表達(dá)式或類型名緊跟 .self 組成,其形式如下:

表達(dá)式.self
類型.self

第一種形式返回表達(dá)式的值。例如:x.self 返回 x。

第二種形式返回相應(yīng)的類型。我們可以用它來獲取某個(gè)實(shí)例的類型作為一個(gè)值來使用。例如,SomeClass.self 會(huì)返回 SomeClass 類型本身,你可以將其傳遞給相應(yīng)函數(shù)或者方法作為參數(shù)。

后綴 self 表達(dá)式語法
后綴 self 表達(dá)式后綴表達(dá)式 . self

dynamicType 表達(dá)式

dynamicType 表達(dá)式由類似函數(shù)調(diào)用表達(dá)式(Function Call Expression)的特殊語法表達(dá)式組成,形式如下:

type(of:表達(dá)式)

上述形式中的表達(dá)式不能是類型名。type(of:) 表達(dá)式會(huì)返回某個(gè)實(shí)例在運(yùn)行時(shí)的類型,具體請看下面的例子:

class SomeBaseClass {
    class func printClassName() {
        print("SomeBaseClass")
    }
}
class SomeSubClass: SomeBaseClass {
    override class func printClassName() {
        print("SomeSubClass")
    }
}
let someInstance: SomeBaseClass = SomeSubClass()
// someInstance 在編譯時(shí)的靜態(tài)類型為 SomeBaseClass,
// 在運(yùn)行時(shí)的動(dòng)態(tài)類型為 SomeSubClass
type(of: someInstance).printClassName()
// 打印 “SomeSubClass”

動(dòng)態(tài)類型表達(dá)式語法
動(dòng)態(tài)類型表達(dá)式 → type(of:表達(dá)式) . dynamicType

下標(biāo)表達(dá)式

可通過下標(biāo)表達(dá)式訪問相應(yīng)的下標(biāo),形式如下:

表達(dá)式[索引表達(dá)式]

要獲取下標(biāo)表達(dá)式的值,可將索引表達(dá)式作為下標(biāo)表達(dá)式的參數(shù)來調(diào)用下標(biāo) getter。下標(biāo) setter 的調(diào)用方式與之一樣。

關(guān)于下標(biāo)的聲明,請參閱 協(xié)議下標(biāo)聲明。

下標(biāo)表達(dá)式語法
下標(biāo)表達(dá)式后綴表達(dá)式 [ 表達(dá)式列表 ]

強(qiáng)制取值表達(dá)式

當(dāng)你確定可選值不是 nil 時(shí),可以使用強(qiáng)制取值表達(dá)式來強(qiáng)制解包,形式如下:

表達(dá)式!

如果該表達(dá)式的值不是 nil,則返回解包后的值。否則,拋出運(yùn)行時(shí)錯(cuò)誤。

返回的值可以被修改,無論是修改值本身,還是修改值的成員。例如:

var x: Int? = 0
x!++
// x 現(xiàn)在是 1

var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]
someDictionary["a"]![0] = 100
// someDictionary 現(xiàn)在是 [b: [10, 20], a: [100, 2, 3]]

強(qiáng)制取值語法
強(qiáng)制取值表達(dá)式后綴表達(dá)式 !

可選鏈表達(dá)式

可選鏈表達(dá)式提供了一種使用可選值的便捷方法,形式如下:

表達(dá)式?

后綴 ? 運(yùn)算符會(huì)根據(jù)表達(dá)式生成可選鏈表達(dá)式而不會(huì)改變表達(dá)式的值。

如果某個(gè)后綴表達(dá)式包含可選鏈表達(dá)式,那么它的執(zhí)行過程會(huì)比較特殊。如果該可選鏈表達(dá)式的值是 nil,整個(gè)后綴表達(dá)式會(huì)直接返回 nil。如果該可選鏈表達(dá)式的值不是 nil,則返回可選鏈表達(dá)式解包后的值,并將該值用于后綴表達(dá)式中剩余的表達(dá)式。在這兩種情況下,整個(gè)后綴表達(dá)式的值都會(huì)是可選類型。

如果某個(gè)后綴表達(dá)式中包含了可選鏈表達(dá)式,那么只有最外層的表達(dá)式會(huì)返回一個(gè)可選類型。例如,在下面的例子中,如果 c 不是 nil,那么它的值會(huì)被解包,然后通過 .property 訪問它的屬性,接著進(jìn)一步通過 .performAction() 調(diào)用相應(yīng)方法。整個(gè) c?.property.performAction() 表達(dá)式返回一個(gè)可選類型的值,而不是多重可選類型。

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

上面的例子跟下面的不使用可選鏈表達(dá)式的例子等價(jià):

var result: Bool? = nil
if let unwrappedC = c {
    result = unwrappedC.property.performAction()
}

可選鏈表達(dá)式解包后的值可以被修改,無論是修改值本身,還是修改值的成員。如果可選鏈表達(dá)式的值為 nil,則表達(dá)式右側(cè)的賦值操作不會(huì)被執(zhí)行。例如:

func someFunctionWithSideEffects() -> Int {
    // 譯者注:為了能看出此函數(shù)是否被執(zhí)行,加上了一句打印
    print("someFunctionWithSideEffects") 
    return 42 
}
var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]

someDictionary["not here"]?[0] = someFunctionWithSideEffects()
// someFunctionWithSideEffects 不會(huì)被執(zhí)行
// someDictionary 依然是 ["b": [10, 20], "a": [1, 2, 3]]

someDictionary["a"]?[0] = someFunctionWithSideEffects()
// someFunctionWithSideEffects 被執(zhí)行并返回 42
// someDictionary 現(xiàn)在是 ["b": [10, 20], "a": [42, 2, 3]]

可選鏈表達(dá)式語法
可選鏈表達(dá)式后綴表達(dá)式 ?

? 類型 語句 ?
?