1.0-基礎(chǔ)部分
pragma mark:輸出常量和變量
你可以用print(_:separator:terminator:)函數(shù)來輸出當(dāng)前常量或變量的值:
默認(rèn)情況下,該函數(shù)通過添加換行符來結(jié)束當(dāng)前行。如果不想換行,可以傳遞一個(gè)空字符串給 terminator 參數(shù)--例如,print(someValue, terminator:"") 。
pragma mark:字符串插值
字符串插值是一種構(gòu)建新字符串的方式,可以在其中包含常量、變量、字面量和表達(dá)式。 您插入的字符串字面量的每一項(xiàng)都在以反斜線為前綴的圓括號中:
Swift 用字符串插值(string interpolation)的方式把常量名或者變量名當(dāng)做占位符加入到長字符串中,Swift 會用當(dāng)前常量或變量的值替換這些占位符。將常量或變量名放入圓括號中,并在開括號前使用反斜杠將其轉(zhuǎn)義:
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message 是 "3 times 2.5 is 7.5"
wanrning 注意:插值字符串中寫在括號中的表達(dá)式不能包含非轉(zhuǎn)義反斜杠 (),并且不能包含回車或換行符。不過,插值字符串可以包含其他字面量。
pragma mark: nil
你可以給可選變量賦值為nil來表示它沒有值:
warning 注意:Swift 的 nil 和 Objective-C 中的 nil 并不一樣。在 Objective-C 中,nil 是一個(gè)指向不存在對象的指針。在 Swift 中,nil 不是指針——它是一個(gè)確定的值,用來表示值缺失。任何類型的可選狀態(tài)都可以被設(shè)置為 nil,不只是對象類型。
pragma mark:強(qiáng)制解析 ! forced unwrapping
當(dāng)你確定可選類型確實(shí)包含值之后,你可以在可選的名字后面加一個(gè)感嘆號(!)來獲取值。這個(gè)驚嘆號表示“我知道這個(gè)可選有值,請使用它。”這被稱為可選值的強(qiáng)制解析(forced unwrapping):
warning 注意:
使用 ! 來獲取一個(gè)不存在的可選值會導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。使用 ! 來強(qiáng)制解析值之前,一定要確定可選包含一個(gè)非 nil 的值。
pragma mark:可選綁定 optional binding
使用可選綁定(optional binding)來判斷可選類型是否包含值,如果包含就把值賦給一個(gè)臨時(shí)常量或者變量。可選綁定可以用在 if 和 while 語句中,這條語句不僅可以用來判斷可選類型中是否有值,同時(shí)可以將可選類型中的值賦給一個(gè)常量或者變量。
你可以包含多個(gè)可選綁定或多個(gè)布爾條件在一個(gè) if 語句中,只要使用逗號分開就行。只要有任意一個(gè)可選綁定的值為nil,或者任意一個(gè)布爾條件為false,則整個(gè)if條件判斷為false,這時(shí)你就需要使用嵌套 if 條件語句來處理
warning 注意: 在 if 條件語句中使用常量和變量來創(chuàng)建一個(gè)可選綁定,僅在 if 語句的句中(body)中才能獲取到值。相反,在 guard 語句中使用常量和變量來創(chuàng)建一個(gè)可選綁定,僅在 guard 語句外且在語句后才能獲取到值
pragma mark:隱式解析可選類型
這種類型的可選狀態(tài)被定義為隱式解析可選類型(implicitly unwrapped optionals)。把想要用作可選的類型的后面的問號(String?)改成感嘆號(String!)來聲明一個(gè)隱式解析可選類型。
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感嘆號來獲取值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感嘆號
warning 注意:如果一個(gè)變量之后可能變成nil的話請不要使用隱式解析可選類型。如果你需要在變量的生命周期中判斷是否是nil的話,請使用普通可選類型。
2.0-基本運(yùn)算符
術(shù)語:
運(yùn)算符分為一元、二元和三元運(yùn)算符:
一元運(yùn)算符對單一操作對象操作(如 -a)。一元運(yùn)算符分前置運(yùn)算符和后置運(yùn)算符,前置運(yùn)算符需緊跟在操作對象之前(如 !b),后置運(yùn)算符需緊跟在操作對象之后(如 c!)。
二元運(yùn)算符操作兩個(gè)操作對象(如 2 + 3),是中置的,因?yàn)樗鼈兂霈F(xiàn)在兩個(gè)操作對象之間。
三元運(yùn)算符操作三個(gè)操作對象,和 C 語言一樣,Swift 只有一個(gè)三元運(yùn)算符,就是三目運(yùn)算符(a ? b : c)。
受運(yùn)算符影響的值叫操作數(shù),在表達(dá)式 1 + 2 中,加號 + 是二元運(yùn)算符,它的兩個(gè)操作數(shù)是值 1 和 2。
warning 注意:復(fù)合賦值運(yùn)算沒有返回值
復(fù)合賦值運(yùn)算沒有返回值,let b = a += 2這類代碼是錯(cuò)誤。這不同于上面提到的自增和自減運(yùn)算符。
// 元組比較
當(dāng)元組中的值可以比較時(shí),你也可以使用這些運(yùn)算符來比較它們的大小。例如,因?yàn)?Int 和 String 類型的值可以比較,所以類型為 (Int, String) 的元組也可以被比較。相反,Bool 不能被比較,也意味著存有布爾類型的元組不能被比較。
比較元組大小會按照從左到右、逐值比較的方式,直到發(fā)現(xiàn)有兩個(gè)值不等時(shí)停止。如果所有的值都相等,那么這一對元組我們就稱它們是相等的。例如:
(1, "zebra") < (2, "apple") // true,因?yàn)?1 小于 2
(3, "apple") < (3, "bird") // true,因?yàn)?3 等于 3,但是 apple 小于 bird
(4, "dog") == (4, "dog") // true,因?yàn)?4 等于 4,dog 等于 dog
warning 注意:
Swift 標(biāo)準(zhǔn)庫只能比較七個(gè)以內(nèi)元素的元組比較函數(shù)。如果你的元組元素超過七個(gè)時(shí),你需要自己實(shí)現(xiàn)比較運(yùn)算符。
pragma mark:空合運(yùn)算符
空合運(yùn)算符(a ?? b)將對可選類型 a 進(jìn)行空判斷,如果 a 包含一個(gè)值就進(jìn)行解封,否則就返回一個(gè)默認(rèn)值 b。表達(dá)式 a 必須是 Optional 類型。默認(rèn)值 b 的類型必須要和 a 存儲值的類型保持一致。
空合運(yùn)算符是對以下代碼的簡短表達(dá)方法:
a != nil ? a! : b
warning 如果 a 為非空值(non-nil),那么值 b 將不會被計(jì)算。這也就是所謂的短路求值。
3.0-字符串和字符
字符串:字符串是例如"hello, world","albatross"這樣的有序的Character(字符)類型的值的集合。
字面量:字符串字面量是由雙引號 ("") 包裹著的具有固定順序的文本字符集。 字符串字面量可以用于為常量和變量提供初始值
pragma mark:注意:
swift中您可以通過將一個(gè)特定字符串分配給一個(gè)變量(var 修飾)來對其進(jìn)行修改,或者分配給一個(gè)常量(let 修飾)來保證其不會被修改:
在 Objective-C 和 Cocoa 中,您需要通過選擇兩個(gè)不同的類(NSString和NSMutableString)來指定字符串是否可以被修改。
// Unicode
Unicode是一個(gè)國際標(biāo)準(zhǔn),用于文本的編碼和表示。 它使您可以用標(biāo)準(zhǔn)格式表示來自任意語言幾乎所有的字符,并能夠?qū)ξ谋疚募蚓W(wǎng)頁這樣的外部資源中的字符進(jìn)行讀寫操作。 Swift 的String和Character類型是完全兼容 Unicode 標(biāo)準(zhǔn)的。
注意: Unicode 碼位(code poing) 的范圍是U+0000到U+D7FF或者U+E000到U+10FFFF。Unicode 標(biāo)量不包括 Unicode 代理項(xiàng)(surrogate pair) 碼位,其碼位范圍是U+D800到U+DFFF。
4.0-集合類型
Swift 語言提供Arrays、Sets和Dictionaries三種基本的集合類型用來存儲集合數(shù)據(jù)。數(shù)組(Arrays)是有序數(shù)據(jù)的集。集合(Sets)是無序無重復(fù)數(shù)據(jù)的集。字典(Dictionaries)是無序的鍵值對的集。
pragma mark: Arrays
Array類型還提供一個(gè)可以創(chuàng)建特定大小并且所有數(shù)據(jù)都被默認(rèn)的構(gòu)造方法。我們可以把準(zhǔn)備加入新數(shù)組的數(shù)據(jù)項(xiàng)數(shù)量(count)和適當(dāng)類型的初始值(repeating)傳入數(shù)組構(gòu)造函數(shù)
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles 是一種 [Double] 數(shù)組,等價(jià)于 [0.0, 0.0, 0.0]
//用數(shù)組字面量構(gòu)造數(shù)組
我們可以使用數(shù)組字面量來進(jìn)行數(shù)組構(gòu)造,這是一種用一個(gè)或者多個(gè)數(shù)值構(gòu)造數(shù)組的簡單方法。數(shù)組字面量是一系列由逗號分割并由方括號包含的數(shù)值:
[value 1, value 2, value 3]。
如果我們同時(shí)需要每個(gè)數(shù)據(jù)項(xiàng)的值和索引值,可以使用enumerated()方法來進(jìn)行數(shù)組遍歷。enumerated()返回一個(gè)由每一個(gè)數(shù)據(jù)項(xiàng)索引值和數(shù)據(jù)值組成的元組。我們可以把這個(gè)元組分解成臨時(shí)常量或者變量來進(jìn)行遍歷:
for (index, value) in shoppingList. enumerated() {
print("Item \(String(index + 1)): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas
pragma mark: Sets
集合(Set)用來存儲相同類型并且沒有確定順序的值。當(dāng)集合元素順序不重要時(shí)或者希望確保每個(gè)元素只出現(xiàn)一次時(shí)可以使用集合而不是數(shù)組。
遍歷一個(gè)集合
for genre in sets {
print("\(genre)")
}
Swift 的Set類型沒有確定的順序,為了按照特定順序來遍歷一個(gè)Set中的值可以使用sorted()方法,它將返回一個(gè)有序數(shù)組,這個(gè)數(shù)組的元素排列順序由操作符'<'對元素進(jìn)行比較的結(jié)果來確定.
for genre in sets.sorted() {
print("\(genre)")
}
pragma mark: Dictionaries
//字段遍歷
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
for airportName in airports.values {
print("Airport name: \(airportName)")
}
如果我們只是需要使用某個(gè)字典的鍵集合或者值集合來作為某個(gè)接受Array實(shí)例的 API 的參數(shù),可以直接使用keys或者values屬性構(gòu)造一個(gè)新數(shù)組:
let airportCodes = [String](airports.keys)
// airportCodes 是 ["YYZ", "LHR"]
let airportNames = [String](airports.values)
// airportNames 是 ["Toronto Pearson", "London Heathrow"]
5.0-控制流
Swift提供了多種流程控制結(jié)構(gòu),包括可以多次執(zhí)行任務(wù)的while循環(huán),基于特定條件選擇執(zhí)行不同代碼分支的if、guard和switch語句,還有控制流程跳轉(zhuǎn)到其他代碼位置的break和continue語句。
Swift 還提供了for-in循環(huán),用來更簡單地遍歷數(shù)組(array),字典(dictionary),區(qū)間(range),字符串(string)和其他序列類型。
Swift 的switch語句比 C 語言中更加強(qiáng)大。在 C 語言中,如果某個(gè) case 不小心漏寫了break,這個(gè) case 就會貫穿至下一個(gè) case,Swift 無需寫break,所以不會發(fā)生這種貫穿的情況。case 還可以匹配很多不同的模式,包括間隔匹配(interval match),元組(tuple)和轉(zhuǎn)換到特定類型。switch語句的 case 中匹配的值可以綁定成臨時(shí)常量或變量,在case體內(nèi)使用,也可以用where來描述更復(fù)雜的匹配條件。
pragma mark: For-In 循環(huán)
for index in 1...5 {
print("\(index) times 5 is \(index * 5)")
}
pragma mark: While 循環(huán)
while循環(huán),每次在循環(huán)開始時(shí)計(jì)算條件是否符合;
repeat-while循環(huán),每次在循環(huán)結(jié)束時(shí)計(jì)算條件是否符合。
condition 條件
while condition {
statements
}
repeat {
statements
} while condition
pragma mark:條件語句 if (if else)
pragma mark: Switch
switch語句會嘗試把某個(gè)值與若干個(gè)模式(pattern)進(jìn)行匹配。根據(jù)第一個(gè)匹配成功的模式,switch語句會執(zhí)行對應(yīng)的代碼。當(dāng)有可能的情況較多時(shí),通常用switch語句替換if語句。
swift 中 switch不存在隱式的貫穿,也就是當(dāng)匹配的 case 分支中的代碼執(zhí)行完畢后,程序會終止switch語句,而不會繼續(xù)執(zhí)行下一個(gè) case 分支。即使你沒有寫break,當(dāng)然,在swift中是可以不寫break的,寫了也可以。
注意: 當(dāng)一個(gè)switch分支僅僅包含注釋時(shí),會被報(bào)編譯時(shí)錯(cuò)誤。注釋不是代碼語句而且也不能讓switch分支達(dá)到被忽略的效果。你應(yīng)該使用break來忽略某個(gè)分支。
每一個(gè) case 分支都必須包含至少一條語句。像下面這樣書寫代碼是無效的,因?yàn)榈谝粋€(gè) case 分支是空的:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // 無效,這個(gè)分支下面沒有語句
case "A":
print("The letter A")
default:
print("Not the letter A")
}
// 這段代碼會報(bào)編譯錯(cuò)誤
pragma mark: 控制轉(zhuǎn)移語句
控制轉(zhuǎn)移語句改變你代碼的執(zhí)行順序,通過它可以實(shí)現(xiàn)代碼的跳轉(zhuǎn)。Swift 有五種控制轉(zhuǎn)移語句:
continue
break
fallthrough
return
throw
continue語句告訴一個(gè)循環(huán)體立刻停止本次循環(huán),重新開始下次循環(huán)。就好像在說“本次循環(huán)我已經(jīng)執(zhí)行完了”,但是并不會離開整個(gè)循環(huán)體。
break語句會立刻結(jié)束整個(gè)控制流的執(zhí)行。當(dāng)你想要更早的結(jié)束一個(gè)switch代碼塊或者一個(gè)循環(huán)體時(shí),你都可以使用break語句。
//循環(huán)語句中的 break
當(dāng)在一個(gè)循環(huán)體中使用break時(shí),會立刻中斷該循環(huán)體的執(zhí)行,然后跳轉(zhuǎn)到表示循環(huán)體結(jié)束的大括號(})后的第一行代碼。不會再有本次循環(huán)的代碼被執(zhí)行,也不會再有下次的循環(huán)產(chǎn)生。
//fallthrough 貫穿
Swift 中的switch不會從上一個(gè) case 分支落入到下一個(gè) case 分支中。相反,只要第一個(gè)匹配到的 case 分支完成了它需要執(zhí)行的語句,整個(gè)switch代碼塊完成了它的執(zhí)行。相比之下,C 語言要求你顯式地插入break語句到每個(gè) case 分支的末尾來阻止自動落入到下一個(gè) case 分支中。Swift 的這種避免默認(rèn)落入到下一個(gè)分支中的特性意味著它的switch 功能要比 C 語言的更加清晰和可預(yù)測,可以避免無意識地執(zhí)行多個(gè) case 分支從而引發(fā)的錯(cuò)誤。
如果你確實(shí)需要 C 風(fēng)格的貫穿的特性,你可以在每個(gè)需要該特性的 case 分支中使用fallthrough關(guān)鍵字。下面的例子使用fallthrough來創(chuàng)建一個(gè)數(shù)字的描述語句。
warning 注意: fallthrough關(guān)鍵字不會檢查它下一個(gè)將會落入執(zhí)行的 case 中的匹配條件。fallthrough簡單地使代碼繼續(xù)連接到下一個(gè) case 中的代碼,這和 C 語言標(biāo)準(zhǔn)中的switch語句特性是一樣的。
pragma mark:guard else
不同于if語句,一個(gè)guard語句總是有一個(gè)else從句,如果條件不為真則執(zhí)行else從句中的代碼。
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
}
如果guard語句的條件被滿足,則繼續(xù)執(zhí)行g(shù)uard語句大括號后的代碼。將變量或者常量的可選綁定作為guard語句的條件,都可以保護(hù)guard語句后面的代碼。
如果條件不被滿足,在else分支上的代碼就會被執(zhí)行。這個(gè)分支必須轉(zhuǎn)移控制以退出guard語句出現(xiàn)的代碼段。它可以用控制轉(zhuǎn)移語句如return,break,continue或者throw做這件事,或者調(diào)用一個(gè)不返回的方法或函數(shù),例如fatalError()。
相比于可以實(shí)現(xiàn)同樣功能的if語句,按需使用guard語句會提升我們代碼的可讀性。它可以使你的代碼連貫的被執(zhí)行而不需要將它包在else塊中,它可以使你在緊鄰條件判斷的地方,處理違規(guī)的情況。
pragma mark:檢測 API 可用性
if #available(iOS 10, macOS 10.12, *) {
// 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
} else {
// 使用先前版本的 iOS 和 macOS 的 API
}
6.0-函數(shù)
//1.0無參數(shù)函數(shù)
//盡管這個(gè)函數(shù)沒有參數(shù),但是定義中在函數(shù)名后還是需要一對圓括號。當(dāng)被調(diào)用時(shí),也需要在函數(shù)名后寫一對圓括號。
func sayHelloWorld() -> String {
return "hello, world"
}
pragma mark:2.0多參數(shù)函數(shù) 函數(shù)可以有多種輸入?yún)?shù),這些參數(shù)被包含在函數(shù)的括號之中,以逗號分隔。
func greet(person: String, alreadyGreeted: Bool) -> String {
if alreadyGreeted {
return greetAgain(person: person)
} else {
return greet(person: person)
}
}
pragma mark:3.0無返回值函數(shù)
//因?yàn)檫@個(gè)函數(shù)不需要返回值,所以這個(gè)函數(shù)的定義中沒有返回箭頭(->)和返回類型。
warning 注意 嚴(yán)格上來說,雖然沒有返回值被定義,greet(person:) 函數(shù)依然返回了值。沒有定義返回類型的函數(shù)會返回一個(gè)特殊的Void值。它其實(shí)是一個(gè)空的元組(tuple),沒有任何元素,可以寫成()。
func greet(person: String) {
print("Hello, \(person)!")
}
warning 注意:返回值可以被忽略(不用變量接受),但定義了有返回值的函數(shù)必須返回一個(gè)值,如果在函數(shù)定義底部沒有返回任何值,將導(dǎo)致編譯時(shí)錯(cuò)誤(compile-time error)。
pragma mark:4.0 多重返回值函數(shù) 元組返回
//你可以用元組(tuple)類型讓多個(gè)值作為一個(gè)復(fù)合值從函數(shù)中返回。
func minMax(array: [Int]) -> (min: Int, max: Int) {
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
需要注意的是,元組的成員不需要在元組從函數(shù)中返回時(shí)命名,因?yàn)樗鼈兊拿忠呀?jīng)在函數(shù)返回類型中指定了。//print(minMax(array: [3,6,9,8,6,8,1]).max)
pragma mark:5.0 可選元組返回類型
//如果函數(shù)返回的元組類型有可能整個(gè)元組都“沒有值”,你可以使用可選的( optional ) 元組返回類型反映整個(gè)元組可以是nil的事實(shí)。你可以通過在元組類型的右括號后放置一個(gè)問號來定義一個(gè)可選元組,例如 (Int, Int)? 或 (String, Int, Bool)?
warning 注意 可選元組類型如 (Int, Int)? 與元組包含可選類型如 (Int?, Int?) 是不同的.可選的元組類型,整個(gè)元組是可選的,而不只是元組中的每個(gè)元素值。
func minMax(array: [Int]) -> (min: Int, max: Int)? {
if array.isEmpty { return nil }
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
// 你可以使用可選綁定來檢查 minMax(array:) 函數(shù)返回的是一個(gè)存在的元組值還是 nil:
if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
print("min is \(bounds.min) and max is \(bounds.max)")
}
// 打印 "min is -6 and max is 109"
pragma mark: 6.0函數(shù)的參數(shù)標(biāo)簽 和 參數(shù)名稱
每個(gè)函數(shù)參數(shù)都有一個(gè)參數(shù)標(biāo)簽( argument label )以及一個(gè)參數(shù)名稱( parameter name )。參數(shù)標(biāo)簽在調(diào)用函數(shù)的時(shí)候使用;調(diào)用的時(shí)候需要將函數(shù)的參數(shù)標(biāo)簽寫在對應(yīng)的參數(shù)前面。參數(shù)名稱在函數(shù)的實(shí)現(xiàn)中使用。默認(rèn)情況下,函數(shù)參數(shù)使用參數(shù)名稱來作為它們的參數(shù)標(biāo)簽。
func someFunction(firstParameterName: Int, secondParameterName: Int) {
// 在函數(shù)體內(nèi),firstParameterName 和 secondParameterName 代表參數(shù)中的第一個(gè)和第二個(gè)參數(shù)值
}
你可以在函數(shù)名稱前指定它的參數(shù)標(biāo)簽,中間以空格分隔:
func someFunction(argumentLabel parameterName: Int) {
// 在函數(shù)體內(nèi),parameterName 代表參數(shù)值
}
// 忽略參數(shù)標(biāo)簽
如果你不希望為某個(gè)參數(shù)添加一個(gè)標(biāo)簽,可以使用一個(gè)下劃線(_)來代替一個(gè)明確的參數(shù)標(biāo)簽。
func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
// 在函數(shù)體內(nèi),firstParameterName 和 secondParameterName 代表參數(shù)中的第一個(gè)和第二個(gè)參數(shù)值
}
someFunction(1, secondParameterName: 2)
如果一個(gè)參數(shù)有一個(gè)標(biāo)簽,那么在調(diào)用的時(shí)候必須使用標(biāo)簽來標(biāo)記這個(gè)參數(shù)。
pragma mark:默認(rèn)參數(shù)值
你可以在函數(shù)體中通過給參數(shù)賦值來為任意一個(gè)參數(shù)定義默認(rèn)值(Deafult Value)。當(dāng)默認(rèn)值被定義后,調(diào)用這個(gè)函數(shù)時(shí)可以忽略這個(gè)參數(shù)。
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
// 如果你在調(diào)用時(shí)候不傳第二個(gè)參數(shù),parameterWithDefault 會值為 12 傳入到函數(shù)體中。
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault = 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault = 12
將不帶有默認(rèn)值的參數(shù)放在函數(shù)參數(shù)列表的最前。一般來說,沒有默認(rèn)值的參數(shù)更加的重要,將不帶默認(rèn)值的參數(shù)放在最前保證在函數(shù)調(diào)用時(shí),非默認(rèn)參數(shù)的順序是一致的,同時(shí)也使得相同的函數(shù)在不同情況下調(diào)用時(shí)顯得更為清晰。
pragma mark:可變參數(shù) variadic parameter
一個(gè)可變參數(shù)(variadic parameter)可以接受零個(gè)或多個(gè)值。函數(shù)調(diào)用時(shí),你可以用可變參數(shù)來指定函數(shù)參數(shù)可以被傳入不確定數(shù)量的輸入值。通過在變量類型名后面加入(...)的方式來定義可變參數(shù)。
可變參數(shù)的傳入值在函數(shù)體中變?yōu)榇祟愋偷囊粋€(gè)數(shù)組。例如,一個(gè)叫做 numbers 的 Double... 型可變參數(shù),在函數(shù)體內(nèi)可以當(dāng)做一個(gè)叫 numbers 的 [Double] 型的數(shù)組常量。
func arithmeticMean(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
warning 注意:一個(gè)函數(shù)最多只能擁有一個(gè)可變參數(shù)。
//輸入輸出參數(shù)
函數(shù)參數(shù)默認(rèn)是常量。試圖在函數(shù)體中更改參數(shù)值將會導(dǎo)致編譯錯(cuò)誤(compile-time error)。這意味著你不能錯(cuò)誤地更改參數(shù)值。如果你想要一個(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ù),替換原來的值。
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
warning 注意:輸入輸出參數(shù)和返回值是不一樣的。上面的 swapTwoInts 函數(shù)并沒有定義任何返回值,但仍然修改了 someInt 和 anotherInt 的值。輸入輸出參數(shù)是函數(shù)對函數(shù)體外產(chǎn)生影響的另一種方式。
7.0-閉包
閉包是一個(gè)重要且非常有用的知識點(diǎn),每一點(diǎn)都很重要,需要系統(tǒng)學(xué)習(xí),不可零碎整理。網(wǎng)上文章很多,大家可移步學(xué)習(xí)隨便在網(wǎng)上找了一篇,感覺寫的比較詳細(xì)Swift3
8.0-枚舉
在 Swift 中,枚舉類型是一等(first-class)類型。它們采用了很多在傳統(tǒng)上只被類(class)所支持的特性,例如計(jì)算屬性(computed properties),用于提供枚舉值的附加信息,實(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 {
// 枚舉定義放在這里
}
下面是用枚舉表示指南針?biāo)膫€(gè)方向的例子:
enum CompassPoint {
case north
case south
case east
case west
}
枚舉中定義的值(如 north,south,east和west)是這個(gè)枚舉的成員值(或成員)。你可以使用case關(guān)鍵字來定義一個(gè)新的枚舉成員值。
多個(gè)成員值可以出現(xiàn)在同一行上,用逗號隔開:
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
warning 注意 與 C 和 Objective-C 不同,Swift 的枚舉成員在被創(chuàng)建時(shí)不會被賦予一個(gè)默認(rèn)的整型值。在上面的CompassPoint例子中,north,south,east和west不會被隱式地賦值為0,1,2和3。相反,這些枚舉成員本身就是完備的值,這些值的類型是已經(jīng)明確定義好的CompassPoint類型。
每個(gè)枚舉定義了一個(gè)全新的類型。像 Swift 中其他類型一樣,它們的名字(例如CompassPoint和Planet)應(yīng)該以一個(gè)大寫字母開頭。給枚舉類型起一個(gè)單數(shù)名字而不是復(fù)數(shù)名字,以便于讀起來更加容易理解:
//枚舉 的原始值
作為關(guān)聯(lián)值的替代選擇,枚舉成員可以被默認(rèn)值(稱為原始值)預(yù)填充,這些原始值的類型必須相同。
這是一個(gè)使用 ASCII 碼作為原始值的枚舉:
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
原始值可以是字符串,字符,或者任意整型值或浮點(diǎn)型值。每個(gè)原始值在枚舉聲明中必須是唯一的。
warnng 注意 原始值和關(guān)聯(lián)值是不同的。原始值是在定義枚舉時(shí)被預(yù)先填充的值,像上述三個(gè) ASCII 碼。對于一個(gè)特定的枚舉成員,它的原始值始終不變。關(guān)聯(lián)值是創(chuàng)建一個(gè)基于枚舉成員的常量或變量時(shí)才設(shè)置的值,枚舉成員的關(guān)聯(lián)值可以變化。
//原始值的隱式賦值
在使用原始值為整數(shù)或者字符串類型的枚舉時(shí),不需要顯式地為每一個(gè)枚舉成員設(shè)置原始值,Swift 將會自動為你賦值。
例如,當(dāng)使用整數(shù)作為原始值時(shí),隱式賦值的值依次遞增1。如果第一個(gè)枚舉成員沒有設(shè)置原始值,其原始值將為0。
下面的枚舉是對之前Planet這個(gè)枚舉的一個(gè)細(xì)化,利用整型的原始值來表示每個(gè)行星在太陽系中的順序:
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
在上面的例子中,Plant.mercury的顯式原始值為1,Planet.venus的隱式原始值為2,依次類推
當(dāng)使用字符串作為枚舉類型的原始值時(shí),每個(gè)枚舉成員的隱式原始值為該枚舉成員的名稱。
下面的例子是CompassPoint枚舉的細(xì)化,使用字符串類型的原始值來表示各個(gè)方向的名稱:
enum CompassPoint: String {
case north, south, east, west
}
上面例子中,CompassPoint.south擁有隱式原始值south,依次類推
使用枚舉成員的rawValue屬性可以訪問該枚舉成員的原始值:
let earthsOrder = Planet.earth.rawValue
// earthsOrder 值為 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection 值為 "west"
//使用原始值初始化枚舉實(shí)例
let possiblePlanet = Planet(rawValue: 7) // 其實(shí)也可以理解為利用原始值獲取對應(yīng)的枚舉實(shí)例
9.0-類和結(jié)構(gòu)體
//定義語法
類和結(jié)構(gòu)體有著類似的定義方式。我們通過關(guān)鍵字class和struct來分別表示類和結(jié)構(gòu)體,并在一對大括號中定義它們的具體內(nèi)容:
class SomeClass {
// 在這里定義類
}
struct SomeStructure {
// 在這里定義結(jié)構(gòu)體
}
warning 注意 在你每次定義一個(gè)新類或者結(jié)構(gòu)體的時(shí)候,實(shí)際上你是定義了一個(gè)新的 Swift 類型。因此請使用UpperCamelCase這種方式來命名(如SomeClass和SomeStructure等),以便符合標(biāo)準(zhǔn) Swift 類型的大寫命名風(fēng)格(如String,Int和Bool)。相反的,請使用lowerCamelCase這種方式為屬性和方法命名(如framerate和incrementCount),以便和類型名區(qū)分。
pragma mark:結(jié)構(gòu)體和枚舉是值類型
值類型被賦予給一個(gè)變量、常量或者被傳遞給一個(gè)函數(shù)的時(shí)候,其值會被拷貝。
在之前的章節(jié)中,我們已經(jīng)大量使用了值類型。實(shí)際上,在 Swift 中,所有的基本類型:整數(shù)(Integer)、浮點(diǎn)數(shù)(floating-point)、布爾值(Boolean)、字符串(string)、數(shù)組(array)和字典(dictionary),都是值類型,并且在底層都是以結(jié)構(gòu)體的形式所實(shí)現(xiàn)。
在 Swift 中,所有的結(jié)構(gòu)體和枚舉類型都是值類型。這意味著它們的實(shí)例,以及實(shí)例中所包含的任何值類型屬性,在代碼中傳遞的時(shí)候都會被復(fù)制。
pragma mark:類是引用類型
與值類型不同,引用類型在被賦予到一個(gè)變量、常量或者被傳遞到一個(gè)函數(shù)時(shí),其值不會被拷貝。因此,引用的是已存在的實(shí)例本身而不是其拷貝。
#######pragma mark:恒等運(yùn)算符
因?yàn)轭愂且妙愋?,有可能有多個(gè)常量和變量在幕后同時(shí)引用同一個(gè)類實(shí)例。(對于結(jié)構(gòu)體和枚舉來說,這并不成立。因?yàn)樗鼈冏鳛橹殿愋停诒毁x予到常量、變量或者傳遞到函數(shù)時(shí),其值總是會被拷貝。
如果能夠判定兩個(gè)常量或者變量是否引用同一個(gè)類實(shí)例將會很有幫助。為了達(dá)到這個(gè)目的,Swift 內(nèi)建了兩個(gè)恒等運(yùn)算符:
等價(jià)于(===)
不等價(jià)于(!==)
運(yùn)用這兩個(gè)運(yùn)算符檢測兩個(gè)常量或者變量是否引用同一個(gè)實(shí)例:
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same Resolution instance.")
}
請注意,“等價(jià)于”(用三個(gè)等號表示,===)與“等于”(用兩個(gè)等號表示,==)的不同:
“等價(jià)于”表示兩個(gè)類類型(class type)的常量或者變量引用同一個(gè)類實(shí)例。
“等于”表示兩個(gè)實(shí)例的值“相等”或“相同”,判定時(shí)要遵照設(shè)計(jì)者定義的評判標(biāo)準(zhǔn),因此相對于“相等”來說,這是一種更加合適的叫法。
pragma mark:字符串、數(shù)組、和字典類型的賦值與復(fù)制行為
Swift 中,許多基本類型,諸如String,Array和Dictionary類型均以結(jié)構(gòu)體的形式實(shí)現(xiàn)。這意味著被賦值給新的常量或變量,或者被傳入函數(shù)或方法中時(shí),它們的值會被拷貝。
Objective-C 中NSString,NSArray和NSDictionary類型均以類的形式實(shí)現(xiàn),而并非結(jié)構(gòu)體。它們在被賦值或者被傳入函數(shù)或方法時(shí),不會發(fā)生值拷貝,而是傳遞現(xiàn)有實(shí)例的引用。
warning 注意 以上是對字符串、數(shù)組、字典的“拷貝”行為的描述。在你的代碼中,拷貝行為看起來似乎總會發(fā)生。然而,Swift 在幕后只在絕對必要時(shí)才執(zhí)行實(shí)際的拷貝。Swift 管理所有的值拷貝以確保性能最優(yōu)化,所以你沒必要去回避賦值來保證性能最優(yōu)化
10.0-屬性
pragma mark:存儲屬性
簡單來說,一個(gè)存儲屬性就是存儲在特定類或結(jié)構(gòu)體實(shí)例里的一個(gè)常量或變量。存儲屬性可以是變量存儲屬性(用關(guān)鍵字 var 定義),也可以是常量存儲屬性(用關(guān)鍵字 let 定義)。
struct FixedLengthRange {
var firstValue: Int
let length: Int
}
pragma mark:常量結(jié)構(gòu)體的存儲屬性
如果創(chuàng)建了一個(gè)結(jié)構(gòu)體的實(shí)例并將其賦值給一個(gè)常量,則無法修改該實(shí)例的任何屬性,即使有屬性被聲明為變量也不行:
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// 該區(qū)間表示整數(shù)0,1,2,3
rangeOfFourItems.firstValue = 6
// 盡管 firstValue 是個(gè)變量屬性,這里還是會報(bào)錯(cuò)
//這種行為是由于結(jié)構(gòu)體(struct)屬于值類型。當(dāng)值類型的實(shí)例被聲明為常量的時(shí)候,它的所有屬性也就成了常量。
//屬于引用類型的類(class)則不一樣。把一個(gè)引用類型的實(shí)例賦給一個(gè)常量后,仍然可以修改該實(shí)例的變量屬性。
//延遲存儲屬性
延遲存儲屬性是指當(dāng)?shù)谝淮伪徽{(diào)用的時(shí)候才會計(jì)算其初始值的屬性。在屬性聲明前使用 lazy 來標(biāo)示一個(gè)延遲存儲屬性。
warning 注意 必須將延遲存儲屬性聲明成變量(使用 var 關(guān)鍵字),因?yàn)閷傩缘某跏贾悼赡茉趯?shí)例構(gòu)造完成之后才會得到。而常量屬性在構(gòu)造過程完成之前必須要有初始值,因此無法聲明成延遲屬性。
延遲屬性很有用,當(dāng)屬性的值依賴于在實(shí)例的構(gòu)造過程結(jié)束后才會知道影響值的外部因素時(shí),或者當(dāng)獲得屬性的初始值需要復(fù)雜或大量計(jì)算時(shí),可以只在需要的時(shí)候計(jì)算它。
class DataManager {
lazy var importer = DataImporter()
var data = [String]()
}
//計(jì)算屬性
除存儲屬性外,類、結(jié)構(gòu)體和枚舉可以定義計(jì)算屬性。計(jì)算屬性不直接存儲值,而是提供一個(gè) getter 和一個(gè)可選的 setter,來間接獲取和設(shè)置其他屬性或變量的值。
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
pragma mark:簡化 setter 聲明
如果計(jì)算屬性的 setter 沒有定義表示新值的參數(shù)名,則可以使用默認(rèn)名稱 newValue。下面是使用了簡化 setter 聲明的 Rect 結(jié)構(gòu)體代碼:
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
pragma mark:只讀計(jì)算屬性
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
return width * height * depth
}
}
pragma mark:屬性觀察器
屬性觀察器監(jiān)控和響應(yīng)屬性值的變化,每次屬性被設(shè)置值的時(shí)候都會調(diào)用屬性觀察器,即使新值和當(dāng)前值相同的時(shí)候也不例外。
可以為屬性添加如下的一個(gè)或全部觀察器:
willSet 在新的值被設(shè)置之前調(diào)用
didSet 在新的值被設(shè)置之后立即調(diào)用
willSet 觀察器會將新的屬性值作為常量參數(shù)傳入,在 willSet 的實(shí)現(xiàn)代碼中可以為這個(gè)參數(shù)指定一個(gè)名稱,如果不指定則參數(shù)仍然可用,這時(shí)使用默認(rèn)名稱 newValue 表示。
同樣,didSet 觀察器會將舊的屬性值作為參數(shù)傳入,可以為該參數(shù)命名或者使用默認(rèn)參數(shù)名 oldValue。如果在 didSet 方法中再次對該屬性賦值,那么新值會覆蓋舊的值。
warning 注意 父類的屬性在子類的構(gòu)造器中被賦值時(shí),它在父類中的 willSet 和 didSet 觀察器會被調(diào)用,隨后才會調(diào)用子類的觀察器。在父類初始化方法調(diào)用之前,子類給屬性賦值時(shí),觀察器不會被調(diào)用。
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
11.0-方法
方法是與某些特定類型相關(guān)聯(lián)的函數(shù)。類、結(jié)構(gòu)體、枚舉都可以定義實(shí)例方法;實(shí)例方法為給定類型的實(shí)例封裝了具體的任務(wù)與功能。類、結(jié)構(gòu)體、枚舉也可以定義類型方法;類型方法與類型本身相關(guān)聯(lián)。類型方法與 Objective-C 中的類方法(class methods)相似。
結(jié)構(gòu)體和枚舉能夠定義方法是 Swift 與 C/Objective-C 的主要區(qū)別之一。在 Objective-C 中,類是唯一能定義方法的類型。但在 Swift 中,你不僅能選擇是否要定義一個(gè)類/結(jié)構(gòu)體/枚舉,還能靈活地在你創(chuàng)建的類型(類/結(jié)構(gòu)體/枚舉)上定義方法。
pragma mark:實(shí)例方法
實(shí)例方法是屬于某個(gè)特定類、結(jié)構(gòu)體或者枚舉類型實(shí)例的方法。實(shí)例方法提供訪問和修改實(shí)例屬性的方法或提供與實(shí)例目的相關(guān)的功能,并以此來支撐實(shí)例的功能。
class Counter {
var count = 0
func increment() {
count += 1
}
func increment(by amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
pragma mark: self屬性
類型的每一個(gè)實(shí)例都有一個(gè)隱含屬性叫做self,self完全等同于該實(shí)例本身。你可以在一個(gè)實(shí)例的實(shí)例方法中使用這個(gè)隱含的self屬性來引用當(dāng)前實(shí)例。
使用這條規(guī)則的主要場景是實(shí)例方法的某個(gè)參數(shù)名稱與實(shí)例的某個(gè)屬性名稱相同的時(shí)候。在這種情況下,參數(shù)名稱享有優(yōu)先權(quán),并且在引用屬性時(shí)必須使用一種更嚴(yán)格的方式。這時(shí)你可以使用self屬性來區(qū)分參數(shù)名稱和屬性名稱。
#######pragma mark:類型方法
實(shí)例方法是被某個(gè)類型的實(shí)例調(diào)用的方法。你也可以定義在類型本身上調(diào)用的方法,這種方法就叫做類型方法。在方法的func關(guān)鍵字之前加上關(guān)鍵字static,來指定類型方法。類還可以用關(guān)鍵字class來允許子類重寫父類的方法實(shí)現(xiàn)。
warning 注意 在 Objective-C 中,你只能為 Objective-C 的類類型(classes)定義類型方法(type-level methods)。在 Swift 中,你可以為所有的類、結(jié)構(gòu)體和枚舉定義類型方法。每一個(gè)類型方法都被它所支持的類型顯式包含。
12.0-繼承
pragma mark:重寫
子類可以為繼承來的實(shí)例方法,類方法,實(shí)例屬性,或下標(biāo)提供自己定制的實(shí)現(xiàn)。我們把這種行為叫重寫。
如果要重寫某個(gè)特性,你需要在重寫定義的前面加上override關(guān)鍵字。這么做,你就表明了你是想提供一個(gè)重寫版本,而非錯(cuò)誤地提供了一個(gè)相同的定義。意外的重寫行為可能會導(dǎo)致不可預(yù)知的錯(cuò)誤,任何缺少override關(guān)鍵字的重寫都會在編譯時(shí)被診斷為錯(cuò)誤。
override關(guān)鍵字會提醒 Swift 編譯器去檢查該類的超類(或其中一個(gè)父類)是否有匹配重寫版本的聲明。這個(gè)檢查可以確保你的重寫定義是正確的。
pragma mark:防止重寫
你可以通過把方法,屬性或下標(biāo)標(biāo)記為final來防止它們被重寫,只需要在聲明關(guān)鍵字前加上final修飾符即可(例如:final var,final func,final class func,以及final subscript)。
如果你重寫了帶有final標(biāo)記的方法,屬性或下標(biāo),在編譯時(shí)會報(bào)錯(cuò)。在類擴(kuò)展中的方法,屬性或下標(biāo)也可以在擴(kuò)展的定義里標(biāo)記為 final 的。
你可以通過在關(guān)鍵字class前添加final修飾符(final class)來將整個(gè)類標(biāo)記為 final 的。這樣的類是不可被繼承的,試圖繼承這樣的類會導(dǎo)致編譯報(bào)錯(cuò)。
13.0-構(gòu)造過程
pragma mark:構(gòu)造過程
構(gòu)造過程是使用類、結(jié)構(gòu)體或枚舉類型的實(shí)例之前的準(zhǔn)備過程。在新實(shí)例可用前必須執(zhí)行這個(gè)過程,具體操作包括設(shè)置實(shí)例中每個(gè)存儲型屬性的初始值和執(zhí)行其他必須的設(shè)置或初始化工作。
warning 注意 當(dāng)你為存儲型屬性設(shè)置默認(rèn)值或者在構(gòu)造器中為其賦值時(shí),它們的值是被直接設(shè)置的,不會觸發(fā)任何屬性觀察者。
pragma mark:構(gòu)造器
構(gòu)造器在創(chuàng)建某個(gè)特定類型的新實(shí)例時(shí)被調(diào)用。它的最簡形式類似于一個(gè)不帶任何參數(shù)的實(shí)例方法,以關(guān)鍵字init命名:
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
pragma mark:默認(rèn)屬性值
如前所述,你可以在構(gòu)造器中為存儲型屬性設(shè)置初始值。同樣,你也可以在屬性聲明時(shí)為其設(shè)置默認(rèn)值。
warning 注意 如果一個(gè)屬性總是使用相同的初始值,那么為其設(shè)置一個(gè)默認(rèn)值比每次都在構(gòu)造器中賦值要好。兩種方法的效果是一樣的,只不過使用默認(rèn)值讓屬性的初始化和聲明結(jié)合得更緊密。使用默認(rèn)值能讓你的構(gòu)造器更簡潔、更清晰,且能通過默認(rèn)值自動推導(dǎo)出屬性的類型;同時(shí),它也能讓你充分利用默認(rèn)構(gòu)造器、構(gòu)造器繼承等特性,后續(xù)章節(jié)將講到。
pragma mark:構(gòu)造參數(shù)
自定義構(gòu)造過程時(shí),可以在定義中提供構(gòu)造參數(shù),指定所需值的類型和名字。構(gòu)造參數(shù)的功能和語法跟函數(shù)和方法的參數(shù)相同。
pragma mark:參數(shù)的內(nèi)部名稱和外部名稱
跟函數(shù)和方法參數(shù)相同,構(gòu)造參數(shù)也擁有一個(gè)在構(gòu)造器內(nèi)部使用的參數(shù)名字和一個(gè)在調(diào)用構(gòu)造器時(shí)使用的外部參數(shù)名字。
然而,構(gòu)造器并不像函數(shù)和方法那樣在括號前有一個(gè)可辨別的名字。因此在調(diào)用構(gòu)造器時(shí),主要通過構(gòu)造器中的參數(shù)名和類型來確定應(yīng)該被調(diào)用的構(gòu)造器。正因?yàn)閰?shù)如此重要,如果你在定義構(gòu)造器時(shí)沒有提供參數(shù)的外部名字,Swift 會為構(gòu)造器的每個(gè)參數(shù)自動生成一個(gè)跟內(nèi)部名字相同的外部名。
pragma mark:不帶外部名的構(gòu)造器參數(shù)
如果你不希望為構(gòu)造器的某個(gè)參數(shù)提供外部名字,你可以使用下劃線(_)來顯式描述它的外部名,以此重寫上面所說的默認(rèn)行為。
init(_ celsius: Double){
temperatureInCelsius = celsius
}
pragma mark:可選屬性類型
如果你定制的類型包含一個(gè)邏輯上允許取值為空的存儲型屬性——無論是因?yàn)樗鼰o法在初始化時(shí)賦值,還是因?yàn)樗谥竽硞€(gè)時(shí)間點(diǎn)可以賦值為空——你都需要將它定義為可選類型??蛇x類型的屬性將自動初始化為nil,表示這個(gè)屬性是有意在初始化時(shí)設(shè)置為空的。
pragma mark:構(gòu)造過程中常量屬性的修改
你可以在構(gòu)造過程中的任意時(shí)間點(diǎn)給常量屬性指定一個(gè)值,只要在構(gòu)造過程結(jié)束時(shí)是一個(gè)確定的值。一旦常量屬性被賦值,它將永遠(yuǎn)不可更改。
warning 注意 對于類的實(shí)例來說,它的常量屬性只能在定義它的類的構(gòu)造過程中修改;不能在子類中修改。
pragma mark:默認(rèn)構(gòu)造器
如果結(jié)構(gòu)體或類的所有屬性都有默認(rèn)值,同時(shí)沒有自定義的構(gòu)造器,那么 Swift 會給這些結(jié)構(gòu)體或類提供一個(gè)默認(rèn)構(gòu)造器(default initializers)。這個(gè)默認(rèn)構(gòu)造器將簡單地創(chuàng)建一個(gè)所有屬性值都設(shè)置為默認(rèn)值的實(shí)例。
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
由于ShoppingListItem類中的所有屬性都有默認(rèn)值,且它是沒有父類的基類,它將自動獲得一個(gè)可以為所有屬性設(shè)置默認(rèn)值的默認(rèn)構(gòu)造器(盡管代碼中沒有顯式為name屬性設(shè)置默認(rèn)值,但由于name是可選字符串類型,它將默認(rèn)設(shè)置為nil)。上面例子中使用默認(rèn)構(gòu)造器創(chuàng)造了一個(gè)ShoppingListItem類的實(shí)例(使用ShoppingListItem()形式的構(gòu)造器語法),并將其賦值給變量item。
pragma mark:結(jié)構(gòu)體的逐一成員構(gòu)造器
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
下面例子中定義了一個(gè)結(jié)構(gòu)體Size,它包含兩個(gè)屬性width和height。Swift 可以根據(jù)這兩個(gè)屬性的初始賦值0.0自動推導(dǎo)出它們的類型為Double。
結(jié)構(gòu)體Size自動獲得了一個(gè)逐一成員構(gòu)造器init(width:height:)。你可以用它來為Size創(chuàng)建新的實(shí)例:
pragma mark:類的繼承和構(gòu)造過程
類里面的所有存儲型屬性——包括所有繼承自父類的屬性——都必須在構(gòu)造過程中設(shè)置初始值。
Swift 為類類型提供了兩種構(gòu)造器來確保實(shí)例中所有存儲型屬性都能獲得初始值,它們分別是指定構(gòu)造器和便利構(gòu)造器。
便利構(gòu)造器是類中比較次要的、輔助型的構(gòu)造器。你可以定義便利構(gòu)造器來調(diào)用同一個(gè)類中的指定構(gòu)造器,并為其參數(shù)提供默認(rèn)值。你也可以定義便利構(gòu)造器來創(chuàng)建一個(gè)特殊用途或特定輸入值的實(shí)例。
pragma mark:指定構(gòu)造器和便利構(gòu)造器
指定構(gòu)造器是類中最主要的構(gòu)造器。一個(gè)指定構(gòu)造器將初始化類中提供的所有屬性,并根據(jù)父類鏈往上調(diào)用父類的構(gòu)造器來實(shí)現(xiàn)父類的初始化。
pragma mark:指定構(gòu)造器和便利構(gòu)造器的語法
init(parameters) {
statements
}
pragma mark:便利構(gòu)造器也采用相同樣式的寫法,但需要在init關(guān)鍵字之前放置convenience關(guān)鍵字,并使用空格將它們倆分開:
convenience init(parameters) {
statements
}
pragma mark:可失敗構(gòu)造器
如果一個(gè)類、結(jié)構(gòu)體或枚舉類型的對象,在構(gòu)造過程中有可能失敗,則為其定義一個(gè)可失敗構(gòu)造器。這里所指的“失敗”是指,如給構(gòu)造器傳入無效的參數(shù)值,或缺少某種所需的外部資源,又或是不滿足某種必要的條件等。
為了妥善處理這種構(gòu)造過程中可能會失敗的情況。你可以在一個(gè)類,結(jié)構(gòu)體或是枚舉類型的定義中,添加一個(gè)或多個(gè)可失敗構(gòu)造器。其語法為在init關(guān)鍵字后面添加問號(init?)。
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
pragma mark:必要構(gòu)造器
在類的構(gòu)造器前添加required修飾符表明所有該類的子類都必須實(shí)現(xiàn)該構(gòu)造器:
class SomeClass {
required init() {
// 構(gòu)造器的實(shí)現(xiàn)代碼
}
}
在子類重寫父類的必要構(gòu)造器時(shí),必須在子類的構(gòu)造器前也添加required修飾符,表明該構(gòu)造器要求也應(yīng)用于繼承鏈后面的子類。在重寫父類中必要的指定構(gòu)造器時(shí),不需要添加override修飾符:
class SomeSubclass: SomeClass {
required init() {
// 構(gòu)造器的實(shí)現(xiàn)代碼
}
}
pragma mark:通過閉包或函數(shù)設(shè)置屬性的默認(rèn)值
如果某個(gè)存儲型屬性的默認(rèn)值需要一些定制或設(shè)置,你可以使用閉包或全局函數(shù)為其提供定制的默認(rèn)值。每當(dāng)某個(gè)屬性所在類型的新實(shí)例被創(chuàng)建時(shí),對應(yīng)的閉包或函數(shù)會被調(diào)用,而它們的返回值會當(dāng)做默認(rèn)值賦值給這個(gè)屬性。
這種類型的閉包或函數(shù)通常會創(chuàng)建一個(gè)跟屬性類型相同的臨時(shí)變量,然后修改它的值以滿足預(yù)期的初始狀態(tài),最后返回這個(gè)臨時(shí)變量,作為屬性的默認(rèn)值。
下面介紹了如何用閉包為屬性提供默認(rèn)值:
```
class SomeClass {
let someProperty: SomeType = {
// 在這個(gè)閉包中給 someProperty 創(chuàng)建一個(gè)默認(rèn)值
// someValue 必須和 SomeType 類型相同
return someValue
}()
}
```
注意閉包結(jié)尾的大括號后面接了一對空的小括號。這用來告訴 Swift 立即執(zhí)行此閉包。如果你忽略了這對括號,相當(dāng)于將閉包本身作為值賦值給了屬性,而不是將閉包的返回值賦值給屬性。
warning 注意 如果你使用閉包來初始化屬性,請記住在閉包執(zhí)行時(shí),實(shí)例的其它部分都還沒有初始化。這意味著你不能在閉包里訪問其它屬性,即使這些屬性有默認(rèn)值。同樣,你也不能使用隱式的self屬性,或者調(diào)用任何實(shí)例方法。
14.0-析構(gòu)過程
析構(gòu)器只適用于類類型,當(dāng)一個(gè)類的實(shí)例被釋放之前,析構(gòu)器會被立即調(diào)用。析構(gòu)器用關(guān)鍵字deinit來標(biāo)示,類似于構(gòu)造器要用init來標(biāo)示。
pragma mark:析構(gòu)過程原理
析構(gòu)器是在實(shí)例釋放發(fā)生前被自動調(diào)用。你不能主動調(diào)用析構(gòu)器。子類繼承了父類的析構(gòu)器,并且在子類析構(gòu)器實(shí)現(xiàn)的最后,父類的析構(gòu)器會被自動調(diào)用。即使子類沒有提供自己的析構(gòu)器,父類的析構(gòu)器也同樣會被調(diào)用。
因?yàn)橹钡綄?shí)例的析構(gòu)器被調(diào)用后,實(shí)例才會被釋放,所以析構(gòu)器可以訪問實(shí)例的所有屬性,并且可以根據(jù)那些屬性可以修改它的行為(比如查找一個(gè)需要被關(guān)閉的文件)。
在類的定義中,每個(gè)類最多只能有一個(gè)析構(gòu)器,而且析構(gòu)器不帶任何參數(shù),如下所示:
deinit {
// 執(zhí)行析構(gòu)過程
}
15.0-可選鏈
可選鏈?zhǔn)秸{(diào)用是一種可以在當(dāng)前值可能為nil的可選值上請求和調(diào)用屬性、方法及下標(biāo)的方法。
pragma mark:使用可選鏈?zhǔn)秸{(diào)用代替強(qiáng)制展開
通過在想調(diào)用的屬性、方法、或下標(biāo)的可選值后面放一個(gè)問號(?),可以定義一個(gè)可選鏈。這一點(diǎn)很像在可選值后面放一個(gè)嘆號(!)來強(qiáng)制展開它的值。它們的主要區(qū)別在于當(dāng)可選值為空時(shí)可選鏈?zhǔn)秸{(diào)用只會調(diào)用失敗,然而強(qiáng)制展開將會觸發(fā)運(yùn)行時(shí)錯(cuò)誤。
特別地,可選鏈?zhǔn)秸{(diào)用的返回結(jié)果與原本的返回結(jié)果具有相同的類型,但是被包裝成了一個(gè)可選值。例如,使用可選鏈?zhǔn)秸{(diào)用訪問屬性,當(dāng)可選鏈?zhǔn)秸{(diào)用成功時(shí),如果屬性原本的返回結(jié)果是Int類型,則會變?yōu)镮nt?類型。
pragma mark:連接多層可選鏈?zhǔn)秸{(diào)用
可以通過連接多個(gè)可選鏈?zhǔn)秸{(diào)用在更深的模型層級中訪問屬性、方法以及下標(biāo)。然而,多層可選鏈?zhǔn)秸{(diào)用不會增加返回值的可選層級。
也就是說:
如果你訪問的值不是可選的,可選鏈?zhǔn)秸{(diào)用將會返回可選值。
如果你訪問的值就是可選的,可選鏈?zhǔn)秸{(diào)用不會讓可選返回值變得“更可選”。
因此:
通過可選鏈?zhǔn)秸{(diào)用訪問一個(gè)Int值,將會返回Int?,無論使用了多少層可選鏈?zhǔn)秸{(diào)用。
類似的,通過可選鏈?zhǔn)秸{(diào)用訪問Int?值,依舊會返回Int?值,并不會返回Int??。
16.0-錯(cuò)誤處理
錯(cuò)誤處理(Error handling)是響應(yīng)錯(cuò)誤以及從錯(cuò)誤中恢復(fù)的過程。Swift 提供了在運(yùn)行時(shí)對可恢復(fù)錯(cuò)誤的拋出、捕獲、傳遞和操作的一等公民支持。
某些操作無法保證總是執(zhí)行完所有代碼或總是生成有用的結(jié)果??蛇x類型可用來表示值缺失,但是當(dāng)某個(gè)操作失敗時(shí),最好能得知失敗的原因,從而可以作出相應(yīng)的應(yīng)對。
pragma mark:用 throwing 函數(shù)傳遞錯(cuò)誤
為了表示一個(gè)函數(shù)、方法或構(gòu)造器可以拋出錯(cuò)誤,在函數(shù)聲明的參數(shù)列表之后加上throws關(guān)鍵字。一個(gè)標(biāo)有throws關(guān)鍵字的函數(shù)被稱作throwing 函數(shù)。如果這個(gè)函數(shù)指明了返回值類型,throws關(guān)鍵詞需要寫在箭頭(->)的前面。
func canThrowErrors() throws -> String
func cannotThrowErrors() -> String
一個(gè) throwing 函數(shù)可以在其內(nèi)部拋出錯(cuò)誤,并將錯(cuò)誤傳遞到函數(shù)被調(diào)用時(shí)的作用域。
warning 注意 只有 throwing 函數(shù)可以傳遞錯(cuò)誤。任何在某個(gè)非 throwing 函數(shù)內(nèi)部拋出的錯(cuò)誤只能在函數(shù)內(nèi)部處理。
func vend(itemNamed name: String) throws {
guard let item = inventory[name] else {
throw VendingMachineError.InvalidSelection
}
guard item.count > 0 else {
throw VendingMachineError.OutOfStock
}
guard item.price <= coinsDeposited else {
throw VendingMachineError.InsufficientFunds(coinsNeeded: item.price - coinsDeposited)
}
coinsDeposited -= item.price
var newItem = item
newItem.count -= 1
inventory[name] = newItem
print("Dispensing \(name)")
}
17.0-類型轉(zhuǎn)換
類型轉(zhuǎn)換 可以判斷實(shí)例的類型,也可以將實(shí)例看做是其父類或者子類的實(shí)例。
類型轉(zhuǎn)換在 Swift 中使用 is 和 as 操作符實(shí)現(xiàn)。這兩個(gè)操作符提供了一種簡單達(dá)意的方式去檢查值的類型或者轉(zhuǎn)換它的類型。
你也可以用它來檢查一個(gè)類型是否實(shí)現(xiàn)了某個(gè)協(xié)議,就像在檢驗(yàn)協(xié)議的一致性部分講述的一樣。
//檢查類型
用類型檢查操作符(is)來檢查一個(gè)實(shí)例是否屬于特定子類型。若實(shí)例屬于那個(gè)子類型,類型檢查操作符返回 true,否則返回 false。
var movieCount = 0
var songCount = 0
for item in library {
if item is Movie {
movieCount += 1
} else if item is Song {
songCount += 1
}
}
pragma mark:向下轉(zhuǎn)型
某類型的一個(gè)常量或變量可能在幕后實(shí)際上屬于一個(gè)子類。當(dāng)確定是這種情況時(shí),你可以嘗試向下轉(zhuǎn)到它的子類型,用類型轉(zhuǎn)換操作符(as? 或 as!)。
因?yàn)橄蛳罗D(zhuǎn)型可能會失敗,類型轉(zhuǎn)型操作符帶有兩種不同形式。條件形式as? 返回一個(gè)你試圖向下轉(zhuǎn)成的類型的可選值。強(qiáng)制形式 as! 把試圖向下轉(zhuǎn)型和強(qiáng)制解包(轉(zhuǎn)換結(jié)果結(jié)合為一個(gè)操作。
當(dāng)你不確定向下轉(zhuǎn)型可以成功時(shí),用類型轉(zhuǎn)換的條件形式(as?)。條件形式的類型轉(zhuǎn)換總是返回一個(gè)可選值,并且若下轉(zhuǎn)是不可能的,可選值將是 nil。這使你能夠檢查向下轉(zhuǎn)型是否成功。
只有你可以確定向下轉(zhuǎn)型一定會成功時(shí),才使用強(qiáng)制形式(as!)。當(dāng)你試圖向下轉(zhuǎn)型為一個(gè)不正確的類型時(shí),強(qiáng)制形式的類型轉(zhuǎn)換會觸發(fā)一個(gè)運(yùn)行時(shí)錯(cuò)誤。
warning 注意 轉(zhuǎn)換沒有真的改變實(shí)例或它的值。根本的實(shí)例保持不變;只是簡單地把它作為它被轉(zhuǎn)換成的類型來使用。
pragma mark:Any 和 AnyObject 的類型轉(zhuǎn)換
Swift 為不確定類型提供了兩種特殊的類型別名:
Any 可以表示任何類型,包括函數(shù)類型。
AnyObject 可以表示任何類類型的實(shí)例。
只有當(dāng)你確實(shí)需要它們的行為和功能時(shí)才使用 Any 和 AnyObject。在你的代碼里使用你期望的明確類型總是更好的。
warning 注意 Any類型可以表示所有類型的值,包括可選類型。Swift 會在你用Any類型來表示一個(gè)可選值的時(shí)候,給你一個(gè)警告。如果你確實(shí)想使用Any類型來承載可選值,你可以使用as操作符顯式轉(zhuǎn)換為Any,如下所示:
let optionalNumber: Int? = 3
things.append(optionalNumber) // 警告
things.append(optionalNumber as Any) // 沒有警告
18.0-擴(kuò)展
擴(kuò)展 就是為一個(gè)已有的類、結(jié)構(gòu)體、枚舉類型或者協(xié)議類型添加新功能。這包括在沒有權(quán)限獲取原始源代碼的情況下擴(kuò)展類型的能力(即 逆向建模 )。擴(kuò)展和 Objective-C 中的分類類似。(與 Objective-C 不同的是,Swift 的擴(kuò)展沒有名字。)
Swift 中的擴(kuò)展可以:
添加計(jì)算型屬性和計(jì)算型類型屬性
定義實(shí)例方法和類型方法
提供新的構(gòu)造器
定義下標(biāo)
定義和使用新的嵌套類型
使一個(gè)已有類型符合某個(gè)協(xié)議
在 Swift 中,你甚至可以對協(xié)議進(jìn)行擴(kuò)展,提供協(xié)議要求的實(shí)現(xiàn),或者添加額外的功能,從而可以讓符合協(xié)議的類型擁有這些功能。你可以從協(xié)議擴(kuò)展獲取更多的細(xì)節(jié)。
warning 注意 擴(kuò)展可以為一個(gè)類型添加新的功能,但是不能重寫已有的功能。
//擴(kuò)展語法
使用關(guān)鍵字 extension 來聲明擴(kuò)展:
extension SomeType {
// 為 SomeType 添加的新功能寫到這里
}
可以通過擴(kuò)展來擴(kuò)展一個(gè)已有類型,使其采納一個(gè)或多個(gè)協(xié)議。在這種情況下,無論是類還是結(jié)構(gòu)體,協(xié)議名字的書寫方式完全一樣:
extension SomeType: SomeProtocol, AnotherProctocol {
// 協(xié)議實(shí)現(xiàn)寫到這里
}
通過這種方式添加協(xié)議一致性的詳細(xì)描述請參閱利用擴(kuò)展添加協(xié)議一致性。
warning 注意 如果你通過擴(kuò)展為一個(gè)已有類型添加新功能,那么新功能對該類型的所有已有實(shí)例都是可用的,即使它們是在這個(gè)擴(kuò)展定義之前創(chuàng)建的。
//計(jì)算型屬性
擴(kuò)展可以為已有類型添加計(jì)算型實(shí)例屬性和計(jì)算型類型屬性。下面的例子為 Swift 的內(nèi)建 Double 類型添加了五個(gè)計(jì)算型實(shí)例屬性,從而提供與距離單位協(xié)作的基本支持:
extension Double {
var km: Double { return self * 1_000.0 }
var m : Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1_000.0 }
var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters")
// 打印 “One inch is 0.0254 meters”
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
// 打印 “Three feet is 0.914399970739201 meters”
warning 注意 擴(kuò)展可以添加新的計(jì)算型屬性,但是不可以添加存儲型屬性,也不可以為已有屬性添加屬性觀察器。
19.0-協(xié)議
協(xié)議 定義了一個(gè)藍(lán)圖,規(guī)定了用來實(shí)現(xiàn)某一特定任務(wù)或者功能的方法、屬性,以及其他需要的東西。類、結(jié)構(gòu)體或枚舉都可以遵循協(xié)議,并為協(xié)議定義的這些要求提供具體實(shí)現(xiàn)。某個(gè)類型能夠滿足某個(gè)協(xié)議的要求,就可以說該類型遵循這個(gè)協(xié)議。
除了遵循協(xié)議的類型必須實(shí)現(xiàn)的要求外,還可以對協(xié)議進(jìn)行擴(kuò)展,通過擴(kuò)展來實(shí)現(xiàn)一部分要求或者實(shí)現(xiàn)一些附加功能,這樣遵循協(xié)議的類型就能夠使用這些功能。
//協(xié)議語法
協(xié)議的定義方式與類、結(jié)構(gòu)體和枚舉的定義非常相似:
protocol SomeProtocol {
// 這里是協(xié)議的定義部分
}
要讓自定義類型遵循某個(gè)協(xié)議,在定義類型時(shí),需要在類型名稱后加上協(xié)議名稱,中間以冒號(:)分隔。遵循多個(gè)協(xié)議時(shí),各協(xié)議之間用逗號(,)分隔:
20.0-泛型
泛型代碼讓你能夠根據(jù)自定義的需求,編寫出適用于任意類型、靈活可重用的函數(shù)及類型。它能讓你避免代碼的重復(fù),用一種清晰和抽象的方式來表達(dá)代碼的意圖。
//泛型函數(shù)
泛型函數(shù)可以適用于任何類型,下面的 swapTwoValues(::) 函數(shù)是上面三個(gè)函數(shù)的泛型版本:
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
這個(gè)函數(shù)的泛型版本使用了占位類型名(在這里用字母 T 來表示)來代替實(shí)際類型名(例如 Int、String 或 Double)。占位類型名沒有指明 T 必須是什么類型,但是它指明了 a 和 b 必須是同一類型 T,無論 T 代表什么類型。只有 swapTwoValues(::) 函數(shù)在調(diào)用時(shí),才能根據(jù)所傳入的實(shí)際類型決定 T 所代表的類型。
warning 注意 上面定義的 swapTwoValues(::) 函數(shù)是受 swap(::) 函數(shù)啟發(fā)而實(shí)現(xiàn)的。后者存在于 Swift 標(biāo)準(zhǔn)庫,你可以在你的應(yīng)用程序中使用它。如果你在代碼中需要類似 swapTwoValues(::) 函數(shù)的功能,你可以使用已存在的 swap(::) 函數(shù)。
21.0-訪問控制
訪問控制可以限定其他源文件或模塊中的代碼對你的代碼的訪問級別。這個(gè)特性可以讓我們隱藏代碼的一些實(shí)現(xiàn)細(xì)節(jié),并且可以為其他人可以訪問和使用的代碼提供接口。