Hello Word
在屏幕上打印“Hello, world”,可以用一行代碼實(shí)現(xiàn):
print("Hello, world")
你不需要為了輸入輸出或者字符串處理導(dǎo)入一個(gè)單獨(dú)的庫,你也不需要main
函數(shù),你同樣不需要在每個(gè)語句結(jié)尾寫上分號。
數(shù)據(jù)類型
Swift 包含了以下基礎(chǔ)數(shù)據(jù)類型:Int
表示整型;Double
和 Float
表示浮點(diǎn)型;Bool
表示布爾;String
表示字符串。Swift 還提供了三個(gè)基本的集合類型,Array
數(shù)組,Set
集合和 Dictionary
字典。
Swift 還增加了高階數(shù)據(jù)類型比如元組(Tuple
)。元組可以讓你創(chuàng)建或者傳遞一組數(shù)據(jù),比如作為函數(shù)的返回值時(shí),你可以用一個(gè)元組可以返回多個(gè)值。
Swift 還增加了可選(Optional
)類型,用于處理值缺失的情況。可選表示“那兒有一個(gè)值,并且它等于 x ”或者“那兒沒有值”。可選有點(diǎn)像在 Objective-C 中使用nil
,但是它可以用在任何類型上,不僅僅是類。可選類型比 Objective-C 中的nil
指針更加安全也更具表現(xiàn)力,它是 Swift 許多強(qiáng)大特性的重要組成部分。
非可選類型,要么賦值,要么不賦值(不賦值的話,默認(rèn)不會等于
nil
);換句話說,非可選類型永遠(yuǎn)不能為nil
可以在類型后面加
?
聲明可選類型。調(diào)用時(shí)它可能有值,也可能是nil
,可以用變量!
的形式保證變量有值。
可以在類型后面加
!
聲明可選類型。調(diào)用時(shí)它可能有值,也可能是nil
,區(qū)別于上面,直接用變量
的形式已經(jīng)保證變量有值了。
你可以使用可選的(Optional)元組反映整個(gè)元組可以是 nil
的情況。你可以通過在元組類型的右括號后放置一個(gè)問號來定義一個(gè)可選元組,例如 (Int,Int)?
或 (String,Int,Bool)?
可選元組類型如
(Int,Int)?
與元組包含可選屬性如(Int?,Int?)
是不同的。可選的元組類型,整個(gè)數(shù)組是可選的,而不只是元組中的每個(gè)元素值。
nil
你可以給可選變量賦值為nil
來表示它沒有值
聲明常量和變量
用let
來聲明常量,用var
來聲明變量。
類型標(biāo)注
當(dāng)你聲明常量或者變量的時(shí)候可以加上類型標(biāo)注(type annotation
),說明常量或者變量中要存儲的值的類型。
使用 :
號加空格
加類型名
在變量或常量名之后就可以完成類型注解。
注釋
單行注釋:
// 這是一個(gè)注釋
多行注釋:
/* 這是一個(gè),
多行注釋 */
多行注釋可以嵌套:
/* 這是第一個(gè)多行注釋的開頭
/* 這是第二個(gè)被嵌套的多行注釋 */
這是第一個(gè)多行注釋的結(jié)尾 */
類型別名
類型別名(type aliases)就是給現(xiàn)有類型定義另一個(gè)名字。
你可以使用typealias
關(guān)鍵字像使用普通的賦值語句一樣,將某個(gè)已經(jīng)存在的類型賦值為新的名字。
typealias 是單一的,不能將整個(gè)泛型類型進(jìn)行重命名。只有泛型類型的確定性得到保證后,我們才可以重命名。
基本運(yùn)算符(Basic Operators)
賦值運(yùn)算符 =
算術(shù)運(yùn)算符,基本的四則算術(shù)運(yùn)算:
- 加法(
+
) - 減法(
-
) - 乘法(
*
) - 除法(
/
)
求余運(yùn)算符 a % b
自增 ++
和自減 --
一元負(fù)號 -
一元正號 +
復(fù)合賦值
組合加運(yùn)算 +=
等
比較運(yùn)算符
- 等于(
a == b
) - 不等于(
a != b
) - 大于(
a > b
) - 小于(
a < b
) - 大于等于(
a >= b
) - 小于等于(
a <= b
)
三目運(yùn)算符
原型是 問題 ? 答案1 : 答案2
空合運(yùn)算符
空合運(yùn)算符 a ?? b
空合并運(yùn)算符是對以下代碼的簡短表達(dá)方法
a != nil ? a! : b
區(qū)間運(yùn)算符
閉區(qū)間運(yùn)算符 a...b
半開區(qū)間 a..<b
邏輯運(yùn)算
- 邏輯非(
!a
) - 邏輯與(
a && b
) - 邏輯或(
a || b
)
字符串和字符
String 是有序的 Character 類型的值的集合。
字符串是值拷貝,而非引用。
集合類型 (Collection Types)
Swift 語言提供Arrays
、Sets
和Dictionaries
三種基本的集合類型用來存儲集合數(shù)據(jù)。數(shù)組是有序數(shù)據(jù)的集,集合是無序無重復(fù)數(shù)據(jù)的集,字典是無序的鍵值對的集。
- 可以用
Array<T>
也可以用[T]
這樣的簡單語法創(chuàng)建一個(gè)數(shù)組。
創(chuàng)建一個(gè)空數(shù)組
var someInts = [Int]()
創(chuàng)建一個(gè)帶有默認(rèn)值的數(shù)組
var threeDoubles = [Double](count: 3, repeatedValue:0.0)
Swift中的
Set
類型被寫為Set<T>
, 這里的T
表示Set
中允許存儲的類型,和數(shù)組不同的是,集合沒有等價(jià)的簡化形式。Swift 的字典使用
Dictionary<Key, Value>
定義,其中Key
是字典中鍵的數(shù)據(jù)類型,Value
是字典中對應(yīng)于這些鍵所存儲值的數(shù)據(jù)類型。也可以用[Key: Value]
這樣快捷的形式去創(chuàng)建一個(gè)字典類型。
控制流
for...in...
和 for initialization; condition; increment { statements }
while condition { statements }
和 repeat { statements } while condition
if
和 switch
在 Swift 中,當(dāng)匹配的 case 分支中的代碼執(zhí)行完畢后,程序會終止
switch
語句,而不會繼續(xù)執(zhí)行下一個(gè) case 分支。這也就是說,不需要在 case 分支中顯式地使用break
語句。這使得switch
語句更安全、更易用,也避免了因忘記寫break
語句而產(chǎn)生的錯誤。
Swift 有四種控制轉(zhuǎn)移語句。
continue
break
fallthrough
return
throw
強(qiáng)制解包:可以使用 if 語句來檢測一個(gè)可選類型是否包含一個(gè)特定的值,如果一個(gè)可選類型確實(shí)包含一個(gè)值,在 if 語句中它將返回 true,否則返回 false。如果你已經(jīng)檢測確認(rèn)該值存在,那么可以使用或者輸出它,在輸出的時(shí)候只需要在名稱后面加上感嘆號(
!
)即可,意思是告訴編譯器:我已經(jīng)檢測好這個(gè)值了,可以使用它了。
強(qiáng)制解包,是一種語法,在其它地方也可以使用,并非僅限于此。
選擇綁定:選擇綁定幫助確定一個(gè)可選值是不是包含了一個(gè)值,如果包含,把該值轉(zhuǎn)化成一個(gè)臨時(shí)常量或者變量(語法上使用
let
)。選擇綁定可以用在 if 或 while 語句中,用來在可選類型外部檢查是否有值并提取可能的值。(條件判斷,并不一定需要是 Bool 類型!)
斷言
斷言會在運(yùn)行時(shí)判斷一個(gè)邏輯條件是否為true
。從字面意思來說,斷言“斷言”一個(gè)條件是否為真。你可以使用斷言來保證在運(yùn)行其他代碼之前,某些重要的條件已經(jīng)被滿足。如果條件判斷為true
,代碼運(yùn)行會繼續(xù)進(jìn)行;如果條件判斷為false
,代碼執(zhí)行結(jié)束,你的應(yīng)用被終止。
使用全局函數(shù) assert
來使用斷言調(diào)試,assert 函數(shù)接受一個(gè)布爾表達(dá)式和一個(gè)斷言失敗時(shí)顯示的消息。
帶標(biāo)簽的語句
label name: while condition { statements }
guard 提前退出
像if
語句一樣,guard
的執(zhí)行取決于一個(gè)表達(dá)式的布爾值。我們可以使用guard
語句來要求條件必須為真時(shí),以執(zhí)行guard
語句后的代碼。不同于if
語句,一個(gè)guard
語句總是有一個(gè)else
分句,如果條件不為真則執(zhí)行else
分局中的代碼。
guard condition else xxx 和其它語言里的 xxx unless condition 效果類似。
函數(shù)
沒有參數(shù)名字的函數(shù),復(fù)雜的帶局部和外部參數(shù)名的函數(shù)。參數(shù)可以提供默認(rèn)值,參數(shù)也可以既當(dāng)做傳入?yún)?shù),也當(dāng)做傳出參數(shù)。
每個(gè)函數(shù)都有一種類型,包括函數(shù)的參數(shù)值類型和返回值類型。
函數(shù)可以有多個(gè)輸入?yún)?shù),寫在圓括號中,用逗號分隔。
函數(shù)可以沒有參數(shù),但是定義中在函數(shù)名后還是需要一對圓括號。當(dāng)被調(diào)用時(shí),也需要在函數(shù)名后寫一對圓括號。
函數(shù)可以沒有返回值。
函數(shù)的定義以 func
作為前綴,用返回箭頭 ->
后跟返回類型的名稱的方式來表示返回類型。
嚴(yán)格上來說,沒有定義返回類型的函數(shù)會返回特殊的值,叫
Void
。它其實(shí)是一個(gè)空的元組(tuple),沒有任何元素,可以寫成()
。
Swift 里輸入?yún)?shù)可以做得很復(fù)雜,支持泛型、函數(shù)等,記住一條不變法則:不論嵌套幾層,輸入?yún)?shù)始終要有
()
包裹著。
被調(diào)用時(shí),一個(gè)函數(shù)的返回值可以被忽略。
你可以用元組(tuple)類型讓多個(gè)值作為一個(gè)復(fù)合值從函數(shù)中返回。
函數(shù)參數(shù)都有一個(gè)外部參數(shù)名(external parameter name)和一個(gè)本地參數(shù)名(local parameter name).外部參數(shù)名用來標(biāo)記傳遞給函數(shù)調(diào)用的參數(shù),本地參數(shù)名在實(shí)現(xiàn)函數(shù)的時(shí)候使用。(對于 Ruby 使用者而言,這是新的概念)
func someFunction(firstParameterName: Int, secondParameterName: Int) {
// function body goes here
// firstParameterName and secondParameterName refer to
// the argument values for the first and second parameters
}
someFunction(1, secondParameterName: 2)
一般情況下,第一個(gè)參數(shù)省略其外部參數(shù)名,第二個(gè)以后的參數(shù)使用其本地參數(shù)名作為自己的外部參數(shù)名.所有參數(shù)需要有不同的本地參數(shù)名,但可以共享相同的外部參數(shù)名.
類似 Ruby 里傳遞哈希、或多參數(shù),視覺上更好對應(yīng),自動匹配。只不過,對于 Swift 來說,感覺有點(diǎn)怪。
如果你提供了外部參數(shù)名,那么函數(shù)在被調(diào)用時(shí),必須使用外部參數(shù)名。(記得,以哈希的形式調(diào)用哈~)
func sayHello(to person: String, and anotherPerson: String) -> String {
return "Hello \(person) and \(anotherPerson)!"
}
sayHello(to: "Bill", and: "Ted")
// prints "Hello Bill and Ted!"
如果你不想為第二個(gè)及后續(xù)的參數(shù)設(shè)置參數(shù)名,用一個(gè)下劃線(_)代替一個(gè)明確地參數(shù)名。因?yàn)榈谝粋€(gè)參數(shù)默認(rèn)忽略其外部參數(shù)名稱,明確寫下劃線是多余的。(在這里,既然都忽略了,那干脆直接的、不使用此特性,不行嗎)
你可以在函數(shù)體中為每個(gè)參數(shù)定義默認(rèn)值(Deafult Values)
。當(dāng)默認(rèn)值被定義后,調(diào)用這個(gè)函數(shù)時(shí)可以忽略這個(gè)參數(shù)。
將帶有默認(rèn)值的參數(shù)放在函數(shù)參數(shù)列表的最后。這樣可以保證在函數(shù)調(diào)用時(shí),非默認(rèn)參數(shù)的順序是一致的,同時(shí)使得相同的函數(shù)在不同情況下調(diào)用時(shí)顯得更為清晰。
一個(gè)可變參數(shù)(variadic parameter)
可以接受零個(gè)或多個(gè)值。函數(shù)調(diào)用時(shí),你可以用可變參數(shù)來傳入不確定數(shù)量的輸入?yún)?shù)。通過在變量類型名后面加入(...)
的方式來定義可變參數(shù)。
傳入可變參數(shù)的值在函數(shù)體內(nèi)當(dāng)做這個(gè)類型的一個(gè)數(shù)組。
函數(shù)參數(shù)默認(rèn)是常量。試圖在函數(shù)體中更改參數(shù)值將會導(dǎo)致編譯錯誤。這意味著你不能錯誤地更改參數(shù)值。
但是,有時(shí)候,如果函數(shù)中有傳入?yún)?shù)的變量值副本將是很有用的。你可以通過指定一個(gè)或多個(gè)參數(shù)為變量參數(shù),從而避免自己在函數(shù)中定義新的變量。變量參數(shù)不是常量,你可以在函數(shù)中把它當(dāng)做新的可修改副本來使用。
通過在參數(shù)名前加關(guān)鍵字 var
來定義變量參數(shù)。
對變量參數(shù)所進(jìn)行的修改在函數(shù)調(diào)用結(jié)束后便消失了,并且對于函數(shù)體外是不可見的。變量參數(shù)僅僅存在于函數(shù)調(diào)用的生命周期中。
變量參數(shù),正如上面所述,僅僅能在函數(shù)體內(nèi)被更改。如果你想要一個(gè)函數(shù)可以修改參數(shù)的值,并且想要在這些修改在函數(shù)調(diào)用結(jié)束后仍然存在,那么就應(yīng)該把這個(gè)參數(shù)定義為輸入輸出參數(shù)(In-Out Parameters)。
定義一個(gè)輸入輸出參數(shù)時(shí),在參數(shù)定義前加 inout
關(guān)鍵字。一個(gè)輸入輸出參數(shù)有傳入函數(shù)的值,這個(gè)值被函數(shù)修改,然后被傳出函數(shù),替換原來的值。
你只能將變量作為輸入輸出參數(shù)。你不能傳入常量或者字面量(literal value),因?yàn)檫@些量是不能被修改的。當(dāng)傳入的參數(shù)作為輸入輸出參數(shù)時(shí),需要在參數(shù)前加&
符,表示這個(gè)值可以被函數(shù)修改。(調(diào)用的時(shí)候,變量前加 &
表示傳遞的是引用)
輸入輸出參數(shù)是函數(shù)對函數(shù)體外產(chǎn)生影響的另一種方式。
每個(gè)函數(shù)都有種特定的函數(shù)類型,由函數(shù)的參數(shù)類型和返回類型組成。
使用函數(shù)類型
在 Swift 中,使用函數(shù)類型就像使用其他類型一樣。例如,你可以定義一個(gè)類型為函數(shù)的常量或變量,并將函數(shù)賦值給它:
var mathFunction: (Int, Int) -> Int = addTwoInts
變量是 mathFunction
類型是函數(shù) addTwoInts
而此函數(shù)的輸入、輸出類型分別是 (Int, Int)
和 Int
函數(shù)類型作為參數(shù)類型
你可以用(Int, Int) -> Int
這樣的函數(shù)類型作為另一個(gè)函數(shù)的參數(shù)類型。這樣你可以將函數(shù)的一部分實(shí)現(xiàn)交由給函數(shù)的調(diào)用者。
下面是另一個(gè)例子,正如上面的函數(shù)一樣,同樣是輸出某種數(shù)學(xué)運(yùn)算結(jié)果:
func printMathResult(mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// prints "Result: 8"
被嵌套函數(shù)在調(diào)用時(shí),函數(shù)參數(shù)為
in
前面的代碼,函數(shù)體為in
后面的代碼。
被嵌套函數(shù)的類型是可預(yù)先知道的,并且定義和調(diào)用同時(shí)進(jìn)行。
函數(shù)類型作為返回類型
你可以用函數(shù)類型作為另一個(gè)函數(shù)的返回類型。你需要做的是在返回箭頭(->
)后寫一個(gè)完整的函數(shù)類型。
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
return backwards ? stepBackward : stepForward
}
以第一個(gè) ->
為分隔,輸入?yún)?shù)類型為 (backwards: Bool)
,輸出也就是返回類型為 (Int) -> Int
嵌套函數(shù)
全局函數(shù),定義在全局域中。嵌套函數(shù),定義在別的函數(shù)體中。
閉包
閉包采取如下三種形式之一:
- 全局函數(shù)是一個(gè)有名字但不會捕獲任何值的閉包
- 嵌套函數(shù)是一個(gè)有名字并可以捕獲其封閉函數(shù)域內(nèi)值的閉包
- 閉包表達(dá)式是一個(gè)利用輕量級語法所寫的可以捕獲其上下文中變量或常量值的匿名閉包
sort(_:)
方法需要傳入兩個(gè)參數(shù):
- 已知類型的數(shù)組
- 閉包函數(shù),該閉包函數(shù)需要傳入與數(shù)組元素類型相同的兩個(gè)值,并返回一個(gè)布爾類型值來表明當(dāng)排序結(jié)束后傳入的第一個(gè)參數(shù)排在第二個(gè)參數(shù)前面還是后面。如果第一個(gè)參數(shù)值出現(xiàn)在第二個(gè)參數(shù)值前面,排序閉包函數(shù)需要返回
true
,反之返回false
。
和 Ruby 里
map
等方法一樣的,容易理解。
閉包表達(dá)式語法有如下一般形式:
{ (parameters) -> returnType in
statements
}
parameters
表示輸入?yún)?shù),returnType
表示輸出類型,in
是關(guān)鍵字表示開始執(zhí)行內(nèi)容,statements
表示執(zhí)行內(nèi)容。
根據(jù)上下文推斷類型
實(shí)際上任何情況下,通過內(nèi)聯(lián)閉包表達(dá)式構(gòu)造的閉包作為參數(shù)傳遞給函數(shù)時(shí),都可以推斷出閉包的參數(shù)和返回值類型,這意味著您幾乎不需要利用完整格式構(gòu)造任何內(nèi)聯(lián)閉包。
也就是說,輸入?yún)?shù)的類型和輸出類型是可省的。
reversed = names.sort( { s1, s2 in return s1 > s2 } )
單表達(dá)式閉包隱式返回
單行表達(dá)式閉包可以通過隱藏return
關(guān)鍵字來隱式返回單行表達(dá)式。
參數(shù)名稱縮寫
Swift 自動為內(nèi)聯(lián)函數(shù)提供了參數(shù)名稱縮寫功能,您可以直接通過$0
,$1
,$2
來順序調(diào)用閉包的參數(shù)。
in
關(guān)鍵字也同樣可以被省略,因?yàn)榇藭r(shí)閉包表達(dá)式完全由閉包函數(shù)體構(gòu)成。
運(yùn)算符函數(shù)
reversed = names.sort(>)
省略到極致,真的能看懂嗎?
尾隨閉包
如果您需要將一個(gè)很長的閉包表達(dá)式作為最后一個(gè)參數(shù)傳遞給函數(shù),可以使用尾隨閉包來增強(qiáng)函數(shù)的可讀性。
尾隨閉包是一個(gè)書寫在函數(shù)括號之后的閉包表達(dá)式,函數(shù)支持將其作為最后一個(gè)參數(shù)調(diào)用。
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函數(shù)體部分
}
// 以下是不使用尾隨閉包進(jìn)行函數(shù)調(diào)用
someFunctionThatTakesAClosure({
// 閉包主體部分
})
// 以下是使用尾隨閉包進(jìn)行函數(shù)調(diào)用
someFunctionThatTakesAClosure() {
// 閉包主體部分
}
捕獲值
Swift最簡單的閉包形式是嵌套函數(shù),也就是定義在其他函數(shù)的函數(shù)體內(nèi)的函數(shù)。
嵌套函數(shù)可以捕獲其外部函數(shù)所有的參數(shù)以及定義的常量和變量。
閉包是引用類型
無論您將函數(shù)/閉包賦值給一個(gè)常量還是變量,您實(shí)際上都是將常量/變量的值設(shè)置為對應(yīng)函數(shù)/閉包的引用。
枚舉
在 Swift 中,枚舉類型是一等公民(first-class)。它們采用了很多傳統(tǒng)上只被類(class)所支持的特征,例如計(jì)算型屬性(computed properties),用于提供關(guān)于枚舉當(dāng)前值的附加信息,實(shí)例方法(instance methods),用于提供和枚舉所代表的值相關(guān)聯(lián)的功能。枚舉也可以定義構(gòu)造函數(shù)(initializers)來提供一個(gè)初始值;可以在原始的實(shí)現(xiàn)基礎(chǔ)上擴(kuò)展它們的功能;可以遵守協(xié)議(protocols)來提供標(biāo)準(zhǔn)的功能。
使用enum
關(guān)鍵詞來創(chuàng)建枚舉并且把它們的整個(gè)定義放在一對大括號內(nèi):
enum SomeEnumeration {
// enumeration definition goes here
}
里面的內(nèi)容被稱為 成員值
。
多個(gè)成員值可以出現(xiàn)在同一行上,用逗號隔開。
一旦一個(gè)變量,被賦值為一個(gè)枚舉的成員值
,你可以使用一個(gè)縮寫語法(.
)將其設(shè)置為另一個(gè)成員值
。
你可以使用switch
語句匹配單個(gè)枚舉值。
作為相關(guān)值的另一種選擇,枚舉成員可以被默認(rèn)值賦值(語法上,和普通的賦值一樣),其中這些原始值具有相同的類型(聲明的時(shí)候要標(biāo)注)。
你也可以在枚舉類型開頭加上indirect
關(guān)鍵字來表示它的所有成員都是可遞歸的。
定義:
enum Result<T, U> {
case Success(T)
case Failure(U)
}
枚舉,和類一樣,可以當(dāng)做普通變量的“類型”。
調(diào)用:
let aSuccess : Result<Int, String> = .Success(123)let aFailure : Result<Int, String> = .Failure("temperature too high")
注意:定義時(shí)對泛型的支持,調(diào)用時(shí)對泛型的指定。
類和結(jié)構(gòu)體
類和結(jié)構(gòu)體對比
Swift 中類和結(jié)構(gòu)體有很多共同點(diǎn)。共同處在于:
- 定義
屬性
用于存儲值 - 定義
方法
用于提供功能 - 定義
附屬腳本
用于訪問值 - 定義
構(gòu)造器
用于生成初始化值 - 通過
擴(kuò)展
以增加默認(rèn)實(shí)現(xiàn)的功能 - 實(shí)現(xiàn)
協(xié)議
以提供某種標(biāo)準(zhǔn)功能
與結(jié)構(gòu)體相比,類還有如下的附加功能:
-
繼承
允許一個(gè)類繼承另一個(gè)類的特征 -
類型轉(zhuǎn)換
允許在運(yùn)行時(shí)檢查和解釋一個(gè)類實(shí)例的類型 -
解構(gòu)器
允許一個(gè)類實(shí)例釋放任何其所被分配的資源 -
引用計(jì)數(shù)
允許對一個(gè)類的多次引用
定義
類和結(jié)構(gòu)體有著類似的定義方式。我們通過關(guān)鍵字class
和struct
來分別表示類和結(jié)構(gòu)體,并在一對大括號中定義它們的具體內(nèi)容。
生成結(jié)構(gòu)體和類實(shí)例的語法一樣。
通過使用 .
,你可以訪問實(shí)例中所含有的屬性。
你也可以使用點(diǎn)語法為屬性變量賦值。
所有結(jié)構(gòu)體都有一個(gè)自動生成的成員逐一構(gòu)造器,用于初始化新結(jié)構(gòu)體實(shí)例中成員的屬性。新實(shí)例中各個(gè)屬性的初始值可以通過屬性的名稱傳遞到成員逐一構(gòu)造器之中:
let vga = Resolution(width:640, height: 480)
以哈希的形式傳參,創(chuàng)建對應(yīng)的實(shí)例,很正常啊。
結(jié)構(gòu)體和枚舉是值類型
在 Swift 中,所有的結(jié)構(gòu)體和枚舉類型都是值類型。這意味著它們的實(shí)例,以及實(shí)例中所包含的任何值類型屬性,在代碼中傳遞的時(shí)候都會被復(fù)制。
類是引用類型
與值類型不同,引用類型在被賦予到一個(gè)變量、常量或者被傳遞到一個(gè)函數(shù)時(shí),操作的是引用,其并不是拷貝。因此,引用的是已存在的實(shí)例本身而不是其拷貝。
如果能夠判定兩個(gè)常量或者變量是否引用同一個(gè)類實(shí)例將會很有幫助。為了達(dá)到這個(gè)目的,Swift 內(nèi)建了兩個(gè)恒等運(yùn)算符:
恒等運(yùn)算符
- 等價(jià)于 ( === )
- 不等價(jià)于 ( !== )
指針
概念被弱化了,甚至可以說沒有了。
絕大部分的自定義數(shù)據(jù)構(gòu)造都應(yīng)該是類,而非結(jié)構(gòu)體。
Swift 中字符串(String)
,數(shù)組(Array)
和字典(Dictionary)
類型均以結(jié)構(gòu)體的形式實(shí)現(xiàn)。
屬性
存儲屬性可以是變量存儲屬性(用關(guān)鍵字var
定義),也可以是常量存儲屬性(用關(guān)鍵字let
定義)。
如果創(chuàng)建了一個(gè)結(jié)構(gòu)體的實(shí)例并將其賦值給一個(gè)常量,則無法修改該實(shí)例的任何屬性,即使定義了變量存儲屬性。
延遲存儲屬性
第一次被調(diào)用的時(shí)候才會計(jì)算其初始值的屬性。在屬性聲明前使用 lazy
來標(biāo)示一個(gè)延遲存儲屬性。
如果一個(gè)被標(biāo)記為
lazy
的屬性在沒有初始化時(shí)就同時(shí)被多個(gè)線程訪問,則無法保證該屬性只會被初始化一次。
計(jì)算屬性
除存儲屬性外,類、結(jié)構(gòu)體和枚舉可以定義計(jì)算屬性
。計(jì)算屬性不直接存儲值,而是提供一個(gè) getter
和一個(gè)可選的 setter
,來間接獲取和設(shè)置其他屬性或變量的值。
如果計(jì)算屬性的 setter 沒有定義表示新值的參數(shù)名,則可以使用默認(rèn)名稱newValue
。
只讀計(jì)算屬性的聲明可以去掉get
關(guān)鍵字和花括號。
屬性觀察器
屬性觀察器
監(jiān)控和響應(yīng)屬性值的變化,每次屬性被設(shè)置值的時(shí)候都會調(diào)用屬性觀察器,甚至新的值和現(xiàn)在的值相同的時(shí)候也不例外。
可以為屬性添加如下的一個(gè)或全部觀察器:
-
willSet
在新的值被設(shè)置之前調(diào)用 -
didSet
在新的值被設(shè)置之后立即調(diào)用
計(jì)算屬性和屬性觀察器所描述的模式也可以用于全局變量和局部變量。全局變量是在函數(shù)、方法、閉包或任何類型之外定義的變量。局部變量是在函數(shù)、方法或閉包內(nèi)部定義的變量。
類型屬性
為類型本身定義屬性,不管類型有多少個(gè)實(shí)例,這些屬性都只有唯一一份。這種屬性就是類型屬性
。
使用關(guān)鍵字static
來定義類型屬性。
跟實(shí)例的屬性一樣,類型屬性的訪問也是通過點(diǎn)運(yùn)算符來進(jìn)行。但是,類型屬性是通過類型本身來獲取和設(shè)置,而不是通過實(shí)例。
類似“類變量”。
方法
實(shí)例方法是屬于某個(gè)特定類、結(jié)構(gòu)體或者枚舉類型實(shí)例的方法。
實(shí)例方法的語法與函數(shù)完全一致。
和調(diào)用屬性一樣,用點(diǎn)語法(dot syntax)調(diào)用實(shí)例方法。
方法的局部參數(shù)名稱和外部參數(shù)名稱 - 和函數(shù)一樣。
修改方法的外部參數(shù)名稱。
1)你可以自己添加一個(gè)顯式的外部名稱或者用一個(gè)井號(#
)作為第一個(gè)參數(shù)的前綴來把這個(gè)局部名稱當(dāng)作外部名稱使用。
2)相反,如果你不想為方法的第二個(gè)及后續(xù)的參數(shù)提供一個(gè)外部名稱,可以通過使用下劃線(_
)作為該參數(shù)的顯式外部名稱,這樣做將覆蓋默認(rèn)行為。
類型的每一個(gè)實(shí)例都有一個(gè)隱含屬性叫做self
,self
完全等同于該實(shí)例本身。你可以在一個(gè)實(shí)例的實(shí)例方法中使用這個(gè)隱含的self
屬性來引用當(dāng)前實(shí)例。
使用self
屬性來區(qū)分參數(shù)名稱和屬性名稱。
實(shí)例方法的某個(gè)參數(shù)名稱與實(shí)例的某個(gè)屬性名稱相同的時(shí)候。在這種情況下,參數(shù)名稱享有優(yōu)先權(quán),并且在引用屬性時(shí)必須使用一種更嚴(yán)格的方式。
在實(shí)例方法中修改值類型
結(jié)構(gòu)體和枚舉是值類型。一般情況下,值類型的屬性不能在它的實(shí)例方法中被修改。
但是,如果你確實(shí)需要在某個(gè)具體的方法中修改結(jié)構(gòu)體或者枚舉的屬性,你可以選擇變異(mutating)
這個(gè)方法。
要使用變異
方法, 將關(guān)鍵字mutating
放到方法的func
關(guān)鍵字之前就可以了。
在變異方法中給self賦值
變異方法能夠賦給隱含屬性self
一個(gè)全新的實(shí)例。
枚舉的變異方法可以把self
設(shè)置為相同的枚舉類型中不同的成員。
** 類型方法**
聲明結(jié)構(gòu)體和枚舉的類型方法,在方法的func
關(guān)鍵字之前加上關(guān)鍵字static
。
俗稱的“類方法”。
下標(biāo)腳本
下標(biāo)腳本允許你通過在實(shí)例后面的方括號
中傳入一個(gè)或者多個(gè)的索引值來對實(shí)例進(jìn)行訪問和賦值。
與定義實(shí)例方法類似,定義下標(biāo)腳本使用subscript
關(guān)鍵字,顯式聲明入?yún)ⅲㄒ粋€(gè)或多個(gè))和返回類型。與實(shí)例方法不同的是下標(biāo)腳本可以設(shè)定為讀寫或只讀。這種方式又有點(diǎn)像計(jì)算型屬性的getter和setter:
subscript(index: Int) -> Int {
get {
// 返回與入?yún)⑵ヅ涞腎nt類型的值
}
set(newValue) {
// 執(zhí)行賦值操作
}
}
newValue
的類型必須和下標(biāo)腳本定義的返回類型相同。與計(jì)算型屬性相同的是set的入?yún)⒙暶?code>newValue就算不寫,在set代碼塊中依然可以使用默認(rèn)的newValue
這個(gè)變量來訪問新賦的值。
下標(biāo)腳本允許任意數(shù)量的入?yún)⑺饕⑶颐總€(gè)入?yún)㈩愋鸵矝]有限制。下標(biāo)腳本的返回值也可以是任何類型。下標(biāo)腳本可以使用變量參數(shù)和可變參數(shù),但使用寫入讀出(in-out)參數(shù)或給參數(shù)設(shè)置默認(rèn)值都是不允許的。
一個(gè)類或結(jié)構(gòu)體可以根據(jù)自身需要提供多個(gè)下標(biāo)腳本實(shí)現(xiàn),在定義下標(biāo)腳本時(shí)通過入?yún)€(gè)類型進(jìn)行區(qū)分,使用下標(biāo)腳本時(shí)會自動匹配合適的下標(biāo)腳本實(shí)現(xiàn)運(yùn)行,這就是下標(biāo)腳本的重載。
繼承
Swift 中的類并不是從一個(gè)通用的基類繼承而來。如果你不為你定義的類指定一個(gè)超類的話,這個(gè)類就自動成為基類。
如果要重寫某個(gè)特性,你需要在重寫定義的前面加上override
關(guān)鍵字。
在合適的地方,你可以通過使用super
前綴來訪問超類版本的方法,屬性或下標(biāo)腳本。
你可以通過把方法,屬性或下標(biāo)腳本標(biāo)記為final
來防止它們被重寫,只需要在聲明關(guān)鍵字前加上final
特性即可。
構(gòu)造過程
構(gòu)造器在創(chuàng)建某特定類型的新實(shí)例時(shí)調(diào)用。它的最簡形式類似于一個(gè)不帶任何參數(shù)的實(shí)例方法,以關(guān)鍵字init
命名。
你可以在定義構(gòu)造器時(shí)提供構(gòu)造參數(shù),為其提供自定義構(gòu)造所需值的類型和名字。構(gòu)造器參數(shù)的功能和語法跟函數(shù)和方法參數(shù)相同。
Swift 會默認(rèn)為每個(gè)構(gòu)造器的參數(shù)自動生成一個(gè)跟內(nèi)部名字相同的外部名,就相當(dāng)于在每個(gè)構(gòu)造參數(shù)之前加了一個(gè)哈希符號。(這是特性之一,也是和普通方法的區(qū)別之一)
如果你不希望為構(gòu)造器的某個(gè)參數(shù)提供外部名字,你可以使用下劃線(_)來顯示描述它的外部名,以此重寫上面所說的默認(rèn)行為。
指定構(gòu)造器和便利構(gòu)造器
類的指定構(gòu)造器的寫法跟值類型簡單構(gòu)造器一樣:
init(parameters) {
statements
}
便利構(gòu)造器也采用相同樣式的寫法,但需要在init
關(guān)鍵字之前放置convenience
關(guān)鍵字,并使用空格將它們倆分開。
convenience init(parameters) {
statements
}
如果一個(gè)類,結(jié)構(gòu)體或枚舉類型的對象,在構(gòu)造自身的過程中有可能失敗,則為其定義一個(gè)可失敗構(gòu)造器,是非常有必要的。
你可以在一個(gè)類,結(jié)構(gòu)體或是枚舉類型的定義中,添加一個(gè)或多個(gè)可失敗構(gòu)造器。其語法為在init
關(guān)鍵字后面加添問號(init?)
。
帶原始值的枚舉類型會自帶一個(gè)可失敗構(gòu)造器init?(rawValue:)
,該可失敗構(gòu)造器有一個(gè)名為rawValue
的默認(rèn)參數(shù),其類型和枚舉類型的原始值類型一致,如果該參數(shù)的值能夠和枚舉類型成員所帶的原始值匹配,則該構(gòu)造器構(gòu)造一個(gè)帶此原始值的枚舉成員,否則構(gòu)造失敗。
通常來說我們通過在init
關(guān)鍵字后添加問號的方式來定義一個(gè)可失敗構(gòu)造器,但你也可以使用通過在init
后面添加驚嘆號的方式來定義一個(gè)可失敗構(gòu)造器(init!)
,該可失敗構(gòu)造器將會構(gòu)建一個(gè)特定類型的隱式解析可選類型的對象。
你可以在 init?
構(gòu)造器中代理調(diào)用 init!
構(gòu)造器,反之亦然。
你也可以用 init?
重寫 init!
,反之亦然。
你還可以用 init
代理調(diào)用init!
,但這會觸發(fā)一個(gè)斷言:是否 init!
構(gòu)造器會觸發(fā)構(gòu)造失敗?
在類的構(gòu)造器前添加 required
修飾符表明所有該類的子類都必須實(shí)現(xiàn)該構(gòu)造器:
class SomeClass {
required init() {
// 在這里添加該必要構(gòu)造器的實(shí)現(xiàn)代碼
}
}
當(dāng)子類重寫基類的必要構(gòu)造器時(shí),必須在子類的構(gòu)造器前同樣添加required
修飾符以確保當(dāng)其它類繼承該子類時(shí),該構(gòu)造器同為必要構(gòu)造器。在重寫基類的必要構(gòu)造器時(shí),不需要添加override
修飾符:
class SomeSubclass: SomeClass {
required init() {
// 在這里添加子類必要構(gòu)造器的實(shí)現(xiàn)代碼
}
}
如果某個(gè)存儲型屬性的默認(rèn)值需要特別的定制或準(zhǔn)備,你就可以使用閉包或全局函數(shù)來為其屬性提供定制的默認(rèn)值。每當(dāng)某個(gè)屬性所屬的新類型實(shí)例創(chuàng)建時(shí),對應(yīng)的閉包或函數(shù)會被調(diào)用,而它們的返回值會當(dāng)做默認(rèn)值賦值給這個(gè)屬性。
注意閉包結(jié)尾的大括號后面接了一對空的小括號。
析構(gòu)過程
析構(gòu)器只適用于類類型,當(dāng)一個(gè)類的實(shí)例被釋放之前,析構(gòu)器會被立即調(diào)用。析構(gòu)器用關(guān)鍵字deinit
來標(biāo)示,類似于構(gòu)造器要用init
來標(biāo)示。
在類的定義中,每個(gè)類最多只能有一個(gè)析構(gòu)器,而且析構(gòu)器不帶任何參數(shù)。
解決實(shí)例之間的循環(huán)強(qiáng)引用
Swift 提供了兩種辦法用來解決你在使用類的屬性時(shí)所遇到的循環(huán)強(qiáng)引用問題:弱引用(weak reference)和無主引用(unowned reference)。
弱引用不會對其引用的實(shí)例保持強(qiáng)引用,因而不會阻止 ARC 銷毀被引用的實(shí)例。這個(gè)特性阻止了引用變?yōu)檠h(huán)強(qiáng)引用。聲明屬性或者變量時(shí),在前面加上weak
關(guān)鍵字表明這是一個(gè)弱引用。
和弱引用類似,無主引用不會牢牢保持住引用的實(shí)例。和弱引用不同的是,無主引用是永遠(yuǎn)有值的。因此,無主引用總是被定義為非可選類型(non-optional type)。你可以在聲明屬性或者變量時(shí),在前面加上關(guān)鍵字unowned
表示這是一個(gè)無主引用。
捕獲列表中的每一項(xiàng)都由一對元素組成,一個(gè)元素是weak
或unowned
關(guān)鍵字,另一個(gè)元素是類實(shí)例的引用(如self
)或初始化過的變量(如delegate = self.delegate!
)。這些項(xiàng)在方括號中用逗號分開。
如果閉包沒有指明參數(shù)列表或者返回類型,即它們會通過上下文推斷,那么可以把捕獲列表和關(guān)鍵字in
放在閉包最開始的地方。
可空鏈?zhǔn)秸{(diào)用
在鏈?zhǔn)秸{(diào)用過程中 ?
和 !
的使用。
錯誤處理
在Swift中,錯誤用符合ErrorType
協(xié)議的值表示。
Swift枚舉特別適合把一系列相關(guān)的錯誤組合在一起,同時(shí)可以把一些相關(guān)的值和錯誤關(guān)聯(lián)在一起。因此編譯器會為實(shí)現(xiàn)ErrorType
協(xié)議的Swift枚舉類型自動實(shí)現(xiàn)相應(yīng)合成。
通過在函數(shù)或方法聲明的參數(shù)后面加上throws
關(guān)鍵字,表明這個(gè)函數(shù)或方法可以拋出錯誤。如果指定一個(gè)返回值,可以把throws
關(guān)鍵字放在返回箭頭(->)的前面。
在拋出函數(shù)體的任意一個(gè)地方,可以通過throw
語句拋出錯誤。
當(dāng)調(diào)用一個(gè)拋出函數(shù)的時(shí)候,在調(diào)用前面加上try
。這個(gè)關(guān)鍵字表明函數(shù)可以拋出錯誤,而且在try
后面代碼將不會執(zhí)行。
使用do-catch
語句來就捕獲和處理錯誤。
do {
try function that throws
statements
} catch pattern {
statements
}
使用了 do
的其它語法:
do {
let a = Animals.Troll
...
}
在運(yùn)行時(shí),有幾種情況拋出函數(shù)事實(shí)上是不會拋出錯誤的。在這幾種情況下,你可以用forced-try
表達(dá)式來調(diào)用拋出函數(shù)或方法,即使用try!
來代替try
。
使用defer
語句來在執(zhí)行一系列的語句。這樣不管有沒有錯誤發(fā)生,都可以執(zhí)行一些必要的收尾操作。
類型轉(zhuǎn)換
類型轉(zhuǎn)換在 Swift 中使用 is
和 as
操作符實(shí)現(xiàn)。這兩個(gè)操作符提供了一種簡單達(dá)意的方式去檢查值的類型或者轉(zhuǎn)換它的類型。
用類型檢查操作符(is
)來檢查一個(gè)實(shí)例是否屬于特定子類型。若實(shí)例屬于那個(gè)子類型,類型檢查操作符返回 true
,否則返回 false
。
某類型的一個(gè)常量或變量可能在幕后實(shí)際上屬于一個(gè)子類。當(dāng)確定是這種情況時(shí),你可以嘗試向下轉(zhuǎn)到它的子類型,用類型轉(zhuǎn)換操作符(as?
或 as!
)
因?yàn)橄蛳罗D(zhuǎn)型可能會失敗,類型轉(zhuǎn)型操作符帶有兩種不同形式。條件形式(conditional form) as?
返回一個(gè)你試圖向下轉(zhuǎn)成的類型的可選值(optional value)。強(qiáng)制形式 as!
把試圖向下轉(zhuǎn)型和強(qiáng)制解包(force-unwraps)結(jié)果作為一個(gè)混合動作。
轉(zhuǎn)換沒有真的改變實(shí)例或它的值。潛在的根本的實(shí)例保持不變;只是簡單地把它作為它被轉(zhuǎn)換成的類來使用。
Swift為不確定類型提供了兩種特殊類型別名:
-
AnyObject
可以代表任何class類型的實(shí)例。 -
Any
可以表示任何類型,包括方法類型(function types)。
嵌套類型
在外部對嵌套類型的引用,以被嵌套類型的名字為前綴,加上所要引用的屬性名。
擴(kuò)展
擴(kuò)展就是向一個(gè)已有的類、結(jié)構(gòu)體、枚舉類型或者協(xié)議類型添加新功能(functionality)。
Swift 中的擴(kuò)展可以:
- 添加計(jì)算型屬性和計(jì)算型靜態(tài)屬性
- 定義實(shí)例方法和類型方法
- 提供新的構(gòu)造器
- 定義下標(biāo)
- 定義和使用新的嵌套類型
- 使一個(gè)已有類型符合某個(gè)協(xié)議
聲明一個(gè)擴(kuò)展使用關(guān)鍵字extension
1)擴(kuò)展可以向已有類型添加計(jì)算型實(shí)例屬性和計(jì)算型類型屬性。
2)擴(kuò)展可以向已有類型添加新的構(gòu)造器。
3)擴(kuò)展可以向已有類型添加新的實(shí)例方法和類型方法。
4)通過擴(kuò)展添加的實(shí)例方法也可以修改該實(shí)例本身。
5)擴(kuò)展可以向一個(gè)已有類型添加新下標(biāo)。
6)擴(kuò)展可以向已有的類、結(jié)構(gòu)體和枚舉添加新的嵌套類型。
協(xié)議
協(xié)議
定義了一個(gè)藍(lán)圖,規(guī)定了用來實(shí)現(xiàn)某一特定工作或者功能所必需的方法和屬性。類,結(jié)構(gòu)體或枚舉類型都可以遵循協(xié)議,并提供具體實(shí)現(xiàn)來完成協(xié)議定義的方法和功能。任意能夠滿足協(xié)議要求的類型被稱為遵循(conform)
這個(gè)協(xié)議。
協(xié)議的定義方式與類,結(jié)構(gòu)體,枚舉的定義非常相似。
要使類遵循某個(gè)協(xié)議,需要在類型名稱后加上協(xié)議名稱,中間以冒號:
分隔,作為類型定義的一部分。遵循多個(gè)協(xié)議時(shí),各協(xié)議之間用逗號,
分隔。
如果類在遵循協(xié)議的同時(shí)擁有父類,應(yīng)該將父類名放在協(xié)議名之前,以逗號分隔。
1、對屬性的規(guī)定
協(xié)議可以規(guī)定其遵循者
提供特定名稱和類型的實(shí)例屬性(instance property)
或類屬性(type property)
,而不指定是存儲型屬性(stored property)
還是計(jì)算型屬性(calculate property)
。此外還必須指明是只讀的還是可讀可寫的。
** 2、對方法的規(guī)定**
協(xié)議可以要求其遵循者實(shí)現(xiàn)某些指定的實(shí)例方法或類方法。這些方法作為協(xié)議的一部分,像普通的方法一樣放在協(xié)議的定義中,但是不需要大括號和方法體。可以在協(xié)議中定義具有可變參數(shù)的方法,和普通方法的定義方式相同。但是在協(xié)議的方法定義中,不支持參數(shù)默認(rèn)值。
3、對Mutating方法的規(guī)定
有時(shí)需要在方法中改變它的實(shí)例。例如,值類型(結(jié)構(gòu)體,枚舉)的實(shí)例方法中,將mutating
關(guān)鍵字作為函數(shù)的前綴,寫在func
之前,表示可以在該方法中修改它所屬的實(shí)例及其實(shí)例屬性的值。
和 Ruby 里某些方法帶
!
是一個(gè)道理,因?yàn)檎{(diào)用這些方法后,會改變對象本身。或者說,具有破壞性。
4、對構(gòu)造器的規(guī)定
協(xié)議可以要求它的遵循者實(shí)現(xiàn)指定的構(gòu)造器。你可以像書寫普通的構(gòu)造器那樣,在協(xié)議的定義里寫下構(gòu)造器的聲明,但不需要寫花括號和構(gòu)造器的實(shí)體。
協(xié)議類型
盡管協(xié)議本身并不實(shí)現(xiàn)任何功能,但是協(xié)議可以被當(dāng)做類型來使用。
協(xié)議可以像其他普通類型一樣使用,使用場景:
- 作為函數(shù)、方法或構(gòu)造器中的參數(shù)類型或返回值類型
- 作為常量、變量或?qū)傩缘念愋?/li>
- 作為數(shù)組、字典或其他容器中的元素類型
協(xié)議能夠繼承一個(gè)或多個(gè)其他協(xié)議,可以在繼承的協(xié)議基礎(chǔ)上增加新的內(nèi)容要求。協(xié)議的繼承語法與類的繼承相似,多個(gè)被繼承的協(xié)議間用逗號分隔。
類專屬協(xié)議
你可以在協(xié)議的繼承列表中,通過添加 class
關(guān)鍵字, 限制協(xié)議只能適配到類(class)類型。(結(jié)構(gòu)體或枚舉不能遵循該協(xié)議)。該class
關(guān)鍵字必須是第一個(gè)出現(xiàn)在協(xié)議的繼承列表中,其后,才是其他繼承協(xié)議。
協(xié)議合成
有時(shí)候需要同時(shí)遵循多個(gè)協(xié)議。你可以將多個(gè)協(xié)議采用protocol<SomeProtocol, AnotherProtocol>
這樣的格式進(jìn)行組合,稱為協(xié)議合成(protocol composition)
。你可以在<>
中羅列任意多個(gè)你想要遵循的協(xié)議,以逗號分隔。
protocol<SomeProtocol, AnotherProtocol>
前面的protocol
是關(guān)鍵字,<>
里面是各個(gè)協(xié)議的名字。
協(xié)議合成
并不會生成一個(gè)新協(xié)議類型,而是將多個(gè)協(xié)議合成為一個(gè)臨時(shí)的協(xié)議,超出范圍后立即失效。
檢驗(yàn)協(xié)議的一致性
-
is
操作符用來檢查實(shí)例是否遵循
了某個(gè)協(xié)議
-
as?
返回一個(gè)可選值,當(dāng)實(shí)例遵循
協(xié)議時(shí),返回該協(xié)議類型;否則返回nil
-
as
用以強(qiáng)制向下轉(zhuǎn)型,如果強(qiáng)轉(zhuǎn)失敗,會引起運(yùn)行時(shí)錯誤。
協(xié)議可以含有可選成員,其遵循者
可以選擇是否實(shí)現(xiàn)這些成員。在協(xié)議中使用optional
關(guān)鍵字作為前綴來定義可選成員。
為協(xié)議擴(kuò)展添加限制條件
在擴(kuò)展協(xié)議的時(shí)候,可以指定一些限制,只有滿足這些限制的協(xié)議遵循者,才能獲得協(xié)議擴(kuò)展提供的屬性和方法。這些限制寫在協(xié)議名之后,使用where
關(guān)鍵字來描述限制情況。
泛型
函數(shù)功能都是相同的,唯一不同之處就在于傳入的變量類型不同。
泛型函數(shù)名后面跟著的占位類型名字(T)是用尖括號括起來的(<T>
)。
你可支持多個(gè)類型參數(shù),命名在尖括號中,用逗號分開。
當(dāng)你擴(kuò)展一個(gè)泛型類型的時(shí)候,你并不需要在擴(kuò)展的定義中提供類型參數(shù)列表。更加方便的是,原始類型定義中聲明的類型參數(shù)列表在擴(kuò)展里是可以使用的,并且這些來自原始類型中的參數(shù)名稱會被用作原始定義中類型參數(shù)的引用。
泛型函數(shù)使用了占位類型(通常此情況下用字母T
來表示)來代替實(shí)際類型(如Int
、String
或Double
)。
另外一個(gè)不同之處在于這個(gè)泛型函數(shù)名后面跟著的占位類型(T)是用尖括號括起來的(<T>
)。這個(gè)尖括號告訴 Swift 那個(gè)T
是函數(shù)所定義的一個(gè)類型。但因?yàn)?code>T是一個(gè)占位類型,Swift 不會去查找命名為T的實(shí)際類型。
類型約束
你可以寫一個(gè)在一個(gè)類型參數(shù)名后面的類型約束,通過冒號分割,來作為類型參數(shù)鏈的一部分。
關(guān)聯(lián)類型
當(dāng)定義一個(gè)協(xié)議時(shí),有的時(shí)候聲明一個(gè)或多個(gè)關(guān)聯(lián)類型作為協(xié)議定義的一部分是非常有用的。一個(gè)關(guān)聯(lián)類型作為協(xié)議的一部分,給定了類型的一個(gè)占位名(或別名)。作用于關(guān)聯(lián)類型上實(shí)際類型在協(xié)議被實(shí)現(xiàn)前是不需要指定的。關(guān)聯(lián)類型被指定為typealias
關(guān)鍵字。
typealias ItemType
。這個(gè)協(xié)議不會定義ItemType
是什么的別名,這個(gè)信息將由任何遵循協(xié)議的類型來提供。
這里的
typealias
和類型別名,意義是不一樣的。遵行此協(xié)議的“類”,總得有對應(yīng)的“類型”吧。但是這個(gè)“類型”具體是什么,又不能確定,那么先用ItemType
代替。(因?yàn)閰f(xié)議里面要用了,不能不提供;僅/只能提供一次)
Where 語句
對關(guān)聯(lián)類型定義約束是非常有用的。你可以在參數(shù)列表中通過where語句定義參數(shù)的約束。一個(gè)where
語句能夠使一個(gè)關(guān)聯(lián)類型遵循一個(gè)特定的協(xié)議,以及(或)那個(gè)特定的類型參數(shù)和關(guān)聯(lián)類型可以是相同的。你可以寫一個(gè)where
語句,緊跟在在類型參數(shù)列表后面,where語句后跟一個(gè)或者多個(gè)針對關(guān)聯(lián)類型的約束,以及(或)一個(gè)或多個(gè)類型和關(guān)聯(lián)類型間的等價(jià)(equality)關(guān)系。
訪問控制
Swift 中的訪問控制模型基于模塊和源文件這兩個(gè)概念。
模塊指的是以獨(dú)立單元構(gòu)建和發(fā)布的Framework
或Application
。在Swift 中的一個(gè)模塊可以使用import
關(guān)鍵字引入另外一個(gè)模塊。
源文件指的是 Swift 中的Swift File
,就是編寫 Swift 源代碼的文件,它通常屬于一個(gè)模塊。盡管一般我們將不同的類
分別定義在不同的源文件中,但是同一個(gè)源文件可以包含多個(gè)類
和函數(shù)
的定義。
Swift 為代碼中的實(shí)體提供了三種不同的訪問級別。這些訪問級別不僅與源文件中定義的實(shí)體相關(guān),同時(shí)也與源文件所屬的模塊相關(guān)。
-
public
:可以訪問自己模塊中源文件里的任何實(shí)體,別人也可以通過引入該模塊來訪問源文件里的所有實(shí)體。通常情況下,Framework
中的某個(gè)接口是可以被任何人使用時(shí),你可以將其設(shè)置為public
級別。 -
internal
:可以訪問自己模塊中源文件里的任何實(shí)體,但是別人不能訪問該模塊中源文件里的實(shí)體。通常情況下,某個(gè)接口或Framework
作為內(nèi)部結(jié)構(gòu)使用時(shí),你可以將其設(shè)置為internal
級別。 -
private
:只能在當(dāng)前源文件中使用的實(shí)體,稱為私有實(shí)體。使用private
級別,可以用作隱藏某些功能的實(shí)現(xiàn)細(xì)節(jié)。
public
為最高級訪問級別,private
為最低級訪問級別。
如果你不為代碼中的所有實(shí)體定義顯式訪問級別,那么它們默認(rèn)為internal
級別。
單元測試目標(biāo)的訪問級別
默認(rèn)情況下只有public
級別的實(shí)體才可以被其他模塊訪問。然而,如果在引入一個(gè)生產(chǎn)模塊時(shí)使用@testable
注解,然后用帶測試的方式編譯這個(gè)生產(chǎn)模塊,單元測試目標(biāo)就可以訪問所有internal
級別的實(shí)體。
高級運(yùn)算符
Swift 中的算術(shù)運(yùn)算符默認(rèn)是不會溢出的。所有溢出行為都會被捕獲并報(bào)告為錯誤。如果想讓系統(tǒng)允許溢出行為,可以選擇使用 Swift 中另一套默認(rèn)支持溢出的運(yùn)算符,比如溢出加法運(yùn)算符(&+
)。所有的這些溢出運(yùn)算符都是以 &
開頭的。
- 溢出加法
&+
- 溢出減法
&-
- 溢出乘法
&*
位運(yùn)算符
1)按位取反運(yùn)算符(~
)
2)按位與運(yùn)算符(&
)
3)按位或運(yùn)算符(|
)
4)按位異或運(yùn)算符(^
)
5)按位左移運(yùn)算符(<<
)和按位右移運(yùn)算符(>>
)
類和結(jié)構(gòu)可以為現(xiàn)有的操作符提供自定義的實(shí)現(xiàn),這通常被稱為運(yùn)算符重載(overloading
)。
類與結(jié)構(gòu)體也能提供標(biāo)準(zhǔn)單目運(yùn)算符(unary operators
)的實(shí)現(xiàn)。單目運(yùn)算符只有一個(gè)操作目標(biāo)。當(dāng)運(yùn)算符出現(xiàn)在操作目標(biāo)之前時(shí),它就是前綴(prefix
)的(比如 -a
),而當(dāng)它出現(xiàn)在操作目標(biāo)之后時(shí),它就是后綴(postfix
)的(比如 i++
)。
要實(shí)現(xiàn)前綴或者后綴運(yùn)算符,需要在聲明運(yùn)算符函數(shù)的時(shí)候在 func
關(guān)鍵字之前指定 prefix
或者 postfix
限定符。
復(fù)合賦值運(yùn)算符(Compound assignment operators
)將賦值運(yùn)算符(=
)與其它運(yùn)算符進(jìn)行結(jié)合。比如,將加法與賦值結(jié)合成加法賦值運(yùn)算符(+=
)。
還可以將賦值與 prefix
或 postfix
限定符結(jié)合起來。
不能對默認(rèn)的賦值運(yùn)算符(
=
)進(jìn)行重載。只有組合賦值符可以被重載。同樣地,也無法對三目條件運(yùn)算符a ? b : c
進(jìn)行重載。
自定義的類和結(jié)構(gòu)體沒有對等價(jià)操作符(equivalence operators
)進(jìn)行默認(rèn)實(shí)現(xiàn),等價(jià)操作符通常被稱為“相等”操作符(==
)與“不等”操作符(!=
)。
新的運(yùn)算符要在全局作用域內(nèi),使用 operator
關(guān)鍵字進(jìn)行聲明,同時(shí)還要指定 prefix
、infix
或者 postfix
限定符。
自定義中綴運(yùn)算符的優(yōu)先級和結(jié)合性
結(jié)合性(associativity
)可取的值有left
,right
和 none
。當(dāng)左結(jié)合運(yùn)算符跟其他相同優(yōu)先級的左結(jié)合運(yùn)算符寫在一起時(shí),會跟左邊的操作數(shù)進(jìn)行結(jié)合。同理,當(dāng)右結(jié)合運(yùn)算符跟其他相同優(yōu)先級的右結(jié)合運(yùn)算符寫在一起時(shí),會跟右邊的操作數(shù)進(jìn)行結(jié)合。而非結(jié)合運(yùn)算符不能跟其他相同優(yōu)先級的運(yùn)算符寫在一起。
結(jié)合性(associativity
)的默認(rèn)值是 none
,優(yōu)先級(precedence
)如果沒有指定,則默認(rèn)為 100
。