前言
SwiftGG
翻譯組的 《The Swift Programming Language》in Chinese 我在 Swift3
的時候通讀過一遍,在 Swift4
的時候只瀏覽了前半部分笔咽,并未通讀±叮現(xiàn)在 Swift5
準備再重新讀一遍文檔,把一些遺忘的馆衔、易忘的椒楣、感覺值得記下的知識點梳理一遍,以方便記憶菩浙。
基礎部分
一般來說,很少需要寫類型注釋(type animation)句伶,如果在聲明常量或者變量時賦了一個初始值劲蜻,
Swift
可以推斷出這個常量或變量的類型。如果需要使用
Swift
保留關鍵字相同的名稱作為常量或變量名考余,可以使用反引號將關鍵字包圍的方式將其作為名字使用先嬉。無論如何,我們都應當避免使用關鍵字作為常量或變量名楚堤,除非別無選擇疫蔓。當遇到一些相關值得簡單分組時,元組是很有用的身冬。元組不適合用來創(chuàng)建復雜的數(shù)據(jù)結構衅胀。如果數(shù)據(jù)結構比較復雜,不要使用元組酥筝,用類或者結構體去建模滚躯。
if
與guard
用戶區(qū)別:在if
條件語句中使用常量或變量來創(chuàng)建一個可選綁定,僅在if
語句的句中body
中才能獲取到值。在guard
語句中使用常量或變量創(chuàng)建一個可選綁定時掸掏,僅在guard
語句外且在語句后才能獲取到值茁影。斷言和先決條件的不同點是,他們什么時候進行狀態(tài)檢測:斷言僅在調(diào)試環(huán)境運行丧凤,而先決條件則在調(diào)試環(huán)境和生產(chǎn)環(huán)境中運行呼胚。在生產(chǎn)環(huán)境中,斷言的條件將不進行評估息裸。這意味著我們可以再開發(fā)階段使用很多斷言蝇更,但是這些斷言再生產(chǎn)環(huán)境都不會產(chǎn)生影響。
基本運算符
在對負數(shù)
b
求余時呼盆,b
的符號會被忽略年扩。這意味著a % -b
和a % b
是一樣的結果。但是-a % b
與a % b
結果并不一樣访圃。-
一元正負號
+
不做任何改變的返回操作數(shù)的值厨幻。 雖然一元操作符什么都不會改變,但當使用一元負號-
來表達負數(shù)時腿时,可以使用一元正好來表達正數(shù)况脆,使代嗎具有對稱美。let minusSix = -6 let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
-
如果兩個元組的元素相同批糟,且長度相同的話格了,元組就可以被比較,比較元組大小會按照從左到右徽鼎、逐值比較的方式盛末,直到發(fā)現(xiàn)兩個值不相等時停止,如果所有值都相等否淤,那么我們就稱這一對元組是相等的悄但。
Swift
標準庫只能比較七個以內(nèi)元素的元組。如果元組元素超過七個石抡,則需要自己實現(xiàn)比較運算符檐嚣。(1, "zebra") < (2, "apple") // true,因為 1 小于 2 (3, "apple") < (3, "bird") // true啰扛,因為 3 等于 3嚎京,但是apple 小于 bird (4, "dog") == (4, "dog") // true,因為 4 等于 4侠讯,dog 等于 dog
- 空合運算符
??
挖藏。ps. 其實我一直我不清除這個叫啥名字。厢漩。
字符串和字符
-
多行字符串字面量,是由一對三個雙引號包裹著的具有固定順序的文本字符集岩臣。也可以在行尾寫一個反斜杠 \ 作為續(xù)行符溜嗜。
let softWrappedQuotation = """ The White Rabbit put on his spectacles. "Where shall I begin, \ please your Majesty?" he asked. "Begin at the beginning," the King said gravely, "and go on \ till you come to the end; then stop." """
- 關于
字符串字面量的特殊字符
,主要是轉義字符、Unicode
標量先鱼。平時使用較少君纫。還有轉義字符反斜杠 \ 的使用。
擴展字符串分隔符
#
土全。將字符串文字放在擴展分隔符中捎琐,這樣字符串中的特殊字符將會被直接包含而不是轉移后的效果。如果想要轉義字符效果裹匙,用等量#
包裹住即可瑞凑。字符串、結構體概页、枚舉籽御,都是值類型。
字符串知識中惰匙,關于
Unicode
技掏,比較零碎,實際開發(fā)中也很少用到项鬼,已讀哑梳。關于計算字符數(shù)量,需要注意的是通過
count
屬性返回的字符數(shù)量并不總是與包含相同自負的NSString
的length
屬性相同绘盟。NSString
的length
屬性是利用UTF-16
表示的十六位代碼單元數(shù)字涧衙,而不是Unicode
可擴展的字符群集。不同的字符可能會占用不同數(shù)量的存儲空間奥此,要以要知道字符串中
Character
的確定位置弧哎,就必須從String
開頭遍歷每一個Unicode
標量到結尾。因此稚虎,Swift
的字符串不能整數(shù)integer
做索引撤嫩。子字符串
SubString
和String
區(qū)別在于性能優(yōu)化上。SubString
可以重用原String
的內(nèi)存空間蠢终,或者另一個SubString
的內(nèi)存空間(String
也有同樣的優(yōu)化序攘,但如果兩個String
共享內(nèi)存的話,它們就會相等)寻拂。這一優(yōu)化意味著你在修改String
和SubString
之前都不需要消耗性能去復制內(nèi)存程奠。就像前面說的那樣,SubString
不適合長期存儲 —— 因為它重用了原String
的內(nèi)存空間祭钉,原String
的內(nèi)存空間必須保留直到它的SubString
不再被使用為止瞄沙。
集合類型
-
關于數(shù)組,每天都在用,要強調(diào)的不多距境。這里說三個方法及區(qū)別:
removeLast()
申尼,移除數(shù)組最后一個元素并返回該元素,數(shù)組必須不為空垫桂,否則會引發(fā)運行時崩潰师幕;popLast()
,移除數(shù)組最后一個可選類型的元素诬滩,也就是說霹粥,數(shù)組可以為空;dropLast()
疼鸟,移除掉最后幾個元素后控,并返回移除后的原數(shù)組。var digitArr = [1,2,3,4,5] var last1 = digitArr.removeLast() var last2 = digitArr.popLast() var remain = digitArr.dropLast(2) print(last1, last2 ?? "-2", remain) // 輸出: 5 4 [1]
集合
Set
愚臀。用來存儲相同類型并且沒有順序的值忆蚀。當集合元素順序不重要并且確保每個元素只出現(xiàn)一次時,可以使用集合而不是數(shù)組姑裂〔鐾啵可自己定義類型作為集合的值類型,但是定義的類型要遵循Hasshable
協(xié)議舶斧,而Hashable
協(xié)議遵循了Equatable
協(xié)議欣鳖,所以定義的類型符合這些協(xié)議即可。-
集合提供了一些方法可以高效完成對集合的操作茴厉。
- 使用
intersection(_:)
方法根據(jù)兩個集合的交集創(chuàng)建一個新的集合泽台。 - 使用
symmetricDifference(_:)
方法根據(jù)兩個集合不相交的值創(chuàng)建一個新的集合。 - 使用
union(_:)
方法根據(jù)兩個集合的所有值創(chuàng)建一個新的集合矾缓。 - 使用
subtracting(_:)
方法根據(jù)不在另一個集合中的值創(chuàng)建一個新的集合怀酷。
- 使用
-
集合還提供了方法來判斷集合之間的關系:
- 使用“是否相等”運算符
==
來判斷兩個集合包含的值是否全部相同。 - 使用
isSubset(of:)
方法來判斷一個集合中的所有值是否也被包含在另外一個集合中嗜闻。 - 使用
isSuperset(of:)
方法來判斷一個集合是否包含另一個集合中所有的值蜕依。 - 使用
isStrictSubset(of:)
或者isStrictSuperset(of:)
方法來判斷一個集合是否是另外一個集合的子集合或者父集合并且兩個集合并不相等。 - 使用
isDisjoint(with:)
方法來判斷兩個集合是否不含有相同的值(是否沒有交集)琉雳。
- 使用“是否相等”運算符
-
字典
Dictionary
是一個無序集合样眠,存儲鍵值之間的關系。為了以特定的順序遍歷字典的鍵或值翠肘,可以對字典的keys
或values
使用sorted()
方法檐束。如果想要移除字典中的相關元素,可以可以使用如下兩種方式:var ages = ["Tom": 20, "jack": 12, "Lilei": 18] ages.removeValue(forKey: "Tom") print(ages) /// ["Lilei": 18, "jack": 12] ages["KK"] = nil print(ages) /// ["Lilei": 18, "jack": 12] ages["jack"] = nil print(ages) /// ["Lilei": 18]
控制流
-
Swift
提供了多種流程控制結構束倍,包括可以多次執(zhí)行任務的While
循環(huán)被丧,基于特定條件選擇執(zhí)行不同代碼分支的if
盟戏、guard
、Switch
語句晚碾,還有控制流程跳轉到其他位置的break
和continue
語句抓半。Swift
還提供了for-in
循環(huán)喂急,用來更簡單的遍歷數(shù)組格嘁、字典、區(qū)間廊移、字符串和其他序列類型糕簿。 -
for-in
循環(huán)中,值得注意的有一點:stride(from:to:by:)
用于半開半閉區(qū)間狡孔,stride(from:through:by:)
用于閉區(qū)間懂诗,都是間隔循環(huán)。示例如下:let minutes = 15 let minuteInterval = 5 for tickMark in stride(from: 0, to: minutes, by: minuteInterval) { print("tickMark = ", tickMark) } /// 輸出: 0 5 10 for tickMark in stride(from: 0, through: minutes, by: minuteInterval) { print("tickMark2 = ", tickMark) } /// 輸出: 0 5 10 15
while
循環(huán)從計算一個條件開始苗膝,如果條件為true
殃恒,會重復運行一段代碼,直到條件變?yōu)?false
辱揭。repeat-while
和while
的區(qū)別在于判斷循環(huán)條件之前离唐,先執(zhí)行一次循環(huán)的代碼塊。然后重復循環(huán)直到條件為false
问窃。Switch
在Swift
中是非常強大亥鬓、靈活的。支持區(qū)間匹配域庇、元組匹配嵌戈、值綁定、where
條件听皿、復合型case
以及包含相同值綁定的復合型case
等熟呛。-
Swift
共有五種控制轉移語句:
continue
,break``fallthrough
尉姨,return
庵朝,throw
。continue
:告訴一個循環(huán)立即停止本次循環(huán)啊送,重新開始下一次的循環(huán)偿短。但是不會離開整個循環(huán)。break
:立刻結束整個控制流的執(zhí)行馋没。break
在循環(huán)中昔逗,是結束該循環(huán)。在Switch
中篷朵,是結束該Switch
勾怒。fallthrough
:在switch
中使用婆排,該關鍵字不會檢查它下一個將會落入執(zhí)行的case
中的匹配條件。fallthrough
簡單地使代碼繼續(xù)連接到下一個case
中的代碼笔链。這和C
語言標準中的switch
特性是一樣的段只。
-
帶標簽的語句。這個我個人確實沒有用到過鉴扫。這里記一下赞枕。簡單來說,就是給語句命名坪创,然后控制轉移語句直接針對特性語句做處理炕婶,下面是代碼:
let finalSquare = 25 var board = [Int](repeating: 0, count: finalSquare + 1) board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08 var square = 0 var diceRoll = 0 gameLoop: while square != finalSquare { diceRoll += 1 if diceRoll == 7 { diceRoll = 1 } switch square + diceRoll { case finalSquare: // 骰子數(shù)剛好使玩家移動到最終的方格里,游戲結束莱预。 break gameLoop case let newSquare where newSquare > finalSquare: // 骰子數(shù)將會使玩家的移動超出最后的方格柠掂,那么這種移動是不合法的,玩家需要重新擲骰子 continue gameLoop default: // 合法移動依沮,做正常的處理 square += diceRoll square += board[square] } } print("Game over!")
提前退出語句
guard
涯贞。-
檢測
API
可用性,只要是針對不同系統(tǒng)版本的兼容危喉。通用寫法:if #available(平臺名稱 版本號, ..., *) { APIs 可用宋渔,語句將執(zhí)行 } else { APIs 不可用,語句將不執(zhí)行 }
實際案例展示:
if #available(iOS 10, macOS 10.12, *) { // 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API } else { // 使用先前版本的 iOS 和 macOS 的 API }
函數(shù)
-
函數(shù)的可變參數(shù)姥饰。一個可變參數(shù)
variadic parameter
可以接受零個或多個值傻谁。通過在變量類型名后面加入...
的方式來定義可變參數(shù)。傳入值在函數(shù)體中變?yōu)榇祟愋偷囊粋€數(shù)組列粪。一個函數(shù)中最多只能有一個可變參數(shù)审磁。func arithmeticMean(_ numbers: Double...) -> Double { var total: Double = 0 for number in numbers { total += number } return total / Double(numbers.count) } arithmeticMean(1, 2, 3, 4, 5) // 返回 3.0, 是這 5 個數(shù)的平均數(shù)。 arithmeticMean(3, 8.25, 18.75) // 返回 10.0, 是這 3 個數(shù)的平均數(shù)岂座。
定義輸入輸出函數(shù)态蒂,在參數(shù)定義前加
inout
關鍵字即可。注意费什,只能傳變量給輸入輸出參數(shù)钾恢,不能傳常量或字面量,因為這些量是不能被修改的鸳址。輸入輸出參數(shù)不能有默認值瘩蚪,而且可變參數(shù)不能使用inout
標記。
閉包
-
關于閉包表達式的進化稿黍。雖然越變越簡單疹瘦,但是,還是應該以清晰明了為主巡球。示例只是對閉包語法的一個說明:
/// 排序數(shù)組 let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] /// 1.0 版本 func backward(_ s1: String, _ s2: String) -> Bool { return s1 > s2 } var reversedNames = names.sorted(by: backward) print(reversedNames) // reversedNames 為 ["Ewa", "Daniella", "Chris", "Barry", "Alex"] /// 2.0 版本 reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 }) /// 3.0 版本 reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } ) /// 4.0 版本 reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } ) /// 5.0 版本 reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } ) /// 6.0 版本 reversedNames = names.sorted(by: { $0 > $1 } ) /// 最終版 reversedNames = names.sorted(by: >)
閉包是引用類型Q糟濉邓嘹!
-
在逃逸閉包內(nèi)必須顯示的調(diào)用
self
,非逃逸閉包則必須要险胰。所謂逃逸閉包汹押,就是指函數(shù)返回后才會執(zhí)行的閉包。典型的就是網(wǎng)絡閉包起便,在請求結束后才會執(zhí)行棚贾。還有自動閉包autoclosure
,但是這個并不建議使用過多缨睡。override func viewDidLoad() { super.viewDidLoad() someFunctionWithNonescapingClosure { x = 50 } someFunctionWithEscapingClosure { self.x = 100 } someFuncationWithAutoclosureClosure(closure: "Hello") } /// 非逃逸閉包 func someFunctionWithNonescapingClosure(closure: () -> Void) { closure() } /// 逃逸閉包 func someFunctionWithEscapingClosure(closure: @escaping () -> Void) { closure() } /// 自動轉化閉包 func someFuncationWithAutoclosureClosure(closure: @autoclosure () -> String) { print("\(closure())") }
枚舉
-
令枚舉遵循
CaseIterable
協(xié)議鸟悴。Swift
會生成一個allCases
屬性陈辱,用于一個包含枚舉所有成員的集合奖年。如:enum Beverage: CaseIterable { case coffee, tea, juice } let numberOfChoices = Beverage.allCases.count print("\(numberOfChoices) beverages available") // 打印“3 beverages available”
-
遞歸枚舉是一種枚舉類型,它由一個或多個枚舉成員使用該枚舉類型的實例作為關聯(lián)值沛贪。用遞歸枚舉時陋守,編譯器會插入一個間接層±常可以在枚舉成員前加上
indirect
來標識該成員可遞歸水评。例如,下面的例子媚送,枚舉存儲了簡單的算術表達式:
enum ArithmeticExpression { case number(Int) indirect case addition(ArithmeticExpression, ArithmeticExpression) indirect case multiplication(ArithmeticExpression, ArithmeticExpression) }
也可以再枚舉類型開頭加上
indirect
關鍵字來表明它的所有成員都是可遞歸的:indirect enum ArithmeticExpression { case number(Int) case addition(ArithmeticExpression, ArithmeticExpression) case multiplication(ArithmeticExpression, ArithmeticExpression) }
類和結構體
-
Swift
中機構體和類有很多共同點中燥,兩者都可以:- 定義屬性有存儲值
- 定義方法用于提供功能
- 定義下標操作用于通過下標訪問它們的值
- 定義構造器用于生成初始值
- 通過擴展增加默認實現(xiàn)以外的功能
- 遵循協(xié)議提供某些標準功能
與結構體相比,類還有如下的附加功能:
- 繼承塘偎,允許一個類繼承另一個類的特征
- 類型轉換疗涉,允許在運行時檢查和解釋一個類實例的類型
- 析構器,允許一個類實例釋放任務其所被分配的資源
- 引用計數(shù)吟秩,允許對一個類多次引用
類增加的附加功能是以增加復雜性為代價的咱扣,作為一般準則,優(yōu)先使用結構體涵防,因為它們更容易理解闹伪,僅在適當或必要時使用類。實際上壮池,這意味著我們大多數(shù)自定義的數(shù)據(jù)類型都應該是結構體和枚舉偏瓤。
-
恒等運算符
因為類是引用類型,所以多個常量或變量幕后可能引用同一個類實例椰憋。對于結構體和枚舉來說厅克,這并不成立,因為他們都是值類型熏矿,再被賦予到常量已骇、變量或者傳遞到函數(shù)時离钝,其值總是會被拷貝。
判定兩個常量或者變量是否引用同一個類實例又是會非常有用褪储。為了達到這個目的卵渴,
Swift
提供了兩個恒等運算符,使用這兩個運算符檢測兩個常量或者變量是否引用同一個類實例:- 相同
===
- 不相同
!==
請注意鲤竹,相同 (用三個等號標識
===
) 與 相等 (用兩個等號表示==
) 的不同浪读,相同 表示兩個類類型(class type
) 的常量或變量是否引用同一個類實例,相等 用于表示兩個值是否相等或等價辛藻,判定時要遵循設計者定義的評判標準碘橘。 - 相同
屬性
全局的常量或者變量都是延遲計算的,跟延時加載存儲屬性相似吱肌,不同的地方在于痘拆,全局的常量或者變量不需要標記
lazy
修飾符。而局部范圍的常量或變量從不延遲計算氮墨。類型屬性纺蛆,不論創(chuàng)建多少一個類的類實例,類型屬性都只有唯一一份规揪,所有實例共享該數(shù)據(jù)桥氏。使用
static
(用于類和結構體),或者class
(用于類)關鍵字作為修飾符猛铅。
方法
- 方法是與某些特性類型相關聯(lián)的函數(shù)字支。譬如實例方法、類方法等奸忽。
-
在實例方法中修改值類型
結構體和枚舉都是值類型堕伪,默認情況下,值類型的屬性不能在它的實例方法中被修改月杉。
但是刃跛,如果確定需要在某個特定的方法中修改結構體或者枚舉的屬性,可以為這個方法選擇 可變
mutating
行為苛萎,然后就可以在方法內(nèi)部改變它的屬性桨昙。在可變方法中可以給
self
重新賦值:enum TriStateSwitch { case off, low, high mutating func next() { switch self { case .off: self = .low case .low: self = .high case .high: self = .off } } } var ovenLight = TriStateSwitch.low ovenLight.next() // ovenLight 現(xiàn)在等于 .high ovenLight.next() // ovenLight 現(xiàn)在等于 .off
下標
-
下標允許通過在實例名稱后面的方括號中傳入一個或者多個索引值來對實例進行查詢。它的語法類似于實例方法語法和計算型屬性語法腌歉。定義下標使用
subscipt
關鍵字蛙酪,與定義實例方法類似,都是指定一個或多個輸入?yún)?shù)和一個返回類型翘盖。與實例方法不同的是桂塞,下標可以設定為讀寫或只讀。這種行為由getter
和setter
實現(xiàn)馍驯,類似計算型屬性:subscript(index: Int) -> Int { get { // 返回一個適當?shù)?Int 類型的值 } set(newValue) { // 執(zhí)行適當?shù)馁x值操作 } }
-
實例下標是在特定類型的一個實例上調(diào)用的下標阁危。也可以定義一種在這個類型自身上調(diào)用的下標玛痊。這種下標被稱作類型下標】翊颍可以通過在
subscript
關鍵字之前寫下static
關鍵字的方式來表示一個類型下標擂煞。類類型可以使用class
關鍵字來代替static
,它允許子類重寫父類中對那個下標的實現(xiàn)趴乡。下面的例子展示了如何定義和調(diào)用一個類型下標:enum Planet: Int { case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune static subscript(n: Int) -> Planet { return Planet(rawValue: n)! } } let mars = Planet[4] print(mars)
-
補充一個
static
與class
的區(qū)別:-
static
可以修飾屬性对省、函數(shù),可用于類或者結構體中晾捏。但是被static
修飾的對象不支持重寫override
蒿涎。 -
class
只用于類對象中,可以重寫惦辛。
-
繼承
- 在合適的地方劳秋,可以通過使用
super
前綴來訪問超類版本的方法、屬性或者下標:- 在方法
someMetho()
的重寫實現(xiàn)中裙品,可以通過super.someMethod
來調(diào)用超類版本的someMethod()
方法俗批。 - 在屬性
someProperty
的getter
或setter
的重寫實現(xiàn)中,可以通過super.someProperty
來訪問超類版本的someProperty
屬性市怎。 - 在下標的重寫實現(xiàn)中,可以通過
super[someIndex]
來訪問超類版本中相同的下標辛慰。
- 在方法
- 可以把方法区匠、屬性、下標標記為
final
來防止它們被重寫帅腌。只需要在聲明關鍵字前加上final
修飾符即可驰弄。也可以在關鍵字class
前添加final
修飾符,來將整個類標記為final
速客。這樣的類是不可以被繼承的戚篙。
構造過程
- 類和結構體在創(chuàng)建實例時,必須為所有存儲屬性設置合適的初始值溺职。存儲屬性不能處于一個未知的狀態(tài)岔擂。當為存儲屬性設置默認值或者在構造器中設置初始值時,他們的值是被直接設置的浪耘,不會觸發(fā)任何屬性觀察者乱灵。
-
關于 形參命名 和 實參標簽
下面代碼塊中,
fromFahrenheit
是實參標簽七冲,fahrenheit
是形參命名痛倚。struct Celsius { var temperatureInCelsius: Double init(fromFahrenheit fahrenheit: Double) { temperatureInCelsius = (fahrenheit - 32.0) / 1.8 } }
可以再構造過程中的任意點給常量屬性賦值,只要在構造過程結束時它設置成正確的值澜躺。一旦長兩屬性被賦值蝉稳,它將永遠不能更改抒蚜。對于類實例來說,它的常量屬性只能在類的構造過程中被修改耘戚,不能在子類中修改削锰。
結構體如果沒有定義任何自定義構造器,它將自動獲得一個 逐一成員構造器(memberwise initializer)毕莱。不像默認構造器器贩,空手存儲型屬性有默認值,結構體也會獲得逐一成員構造器朋截。
構造器可以調(diào)用其他構造器來完成實力部分的構造過程蛹稍。這一過程成為構造器代理。
-
Swift
為類提供了兩種構造器來確保實例中所有的存儲型屬性都能獲得初始值部服,它們被稱為指定構造器和便利構造器唆姐。指定構造器是類中最主要的構造器。其中:- 指定構造器必須總是 向上代理
- 便利構造器必須總是 橫向代理
-
安全檢查的兩段式構造過程展示:
階段 1
- 類的某個指定構造器或便利構造器被調(diào)用廓八。
- 完成類的新實例的內(nèi)存分配奉芦,但此時內(nèi)存還沒有被初始化。
- 指定構造器確保其所在的類引入的所有存儲型屬性都已賦初值剧蹂。存儲型屬性的內(nèi)存完成初始化声功。
- 指定構造器切換到父類的構造器,對其存儲屬性完成同樣的任務宠叼。
- 這個過程沿著類的繼承鏈一直往上執(zhí)行先巴,直到達到繼承鏈的最頂部。
- 當達到了繼承鏈的最頂部冒冬,而且繼承鏈的最后一個類已經(jīng)確保其所有的存儲型屬性已賦初值伸蚯,這個實例的內(nèi)存被認為已經(jīng)完全初始化,此時简烤,階段1完成剂邮。
階段 2
- 從繼承鏈的頂部往下,繼承鏈上每個類的指定構造器都有機會進一步自定義實例横侦。構造器此時可訪問
self
挥萌,修改它的屬性并調(diào)用實例方法等。 - 最終丈咐,繼承鏈中任意的便利構造器都有機會自定義其實例和使用
self
瑞眼。
-
可失敗構造器的參數(shù)名和參數(shù)類型,不能與其他非可失敗構造器的參數(shù)名棵逊,及參數(shù)類型相同伤疙。嚴格來說,構造器都不支持返回值。因為構造器本身的作用徒像,只是確保對象能被正確的構造黍特。因此只是用
return nil
來可失敗構造器構造失敗,而不要用關鍵字return
來表明構造成功锯蛀。struct Animal { let species: String init?(species: String) { if species.isEmpty { return nil } self.species = species } }
析構過程
- 析構器只適用于類類型灭衷,析構器是在實例被釋放前自動調(diào)用的。不能主動調(diào)用析構器旁涤。子類繼承父類的構造器翔曲,并且在子類的構造器實現(xiàn)的最后,父類構造器會被自動調(diào)用劈愚。
可選鏈
- 嗯瞳遍,就是那個
?
。這里只是說明了怎么調(diào)用屬性菌羽、方法掠械、下標等。
錯誤處理
-
Swift
中的錯誤處理和其他語言中用try
注祖、catch
猾蒂、 和throws
進行異常處理很像。和其他語言(包括Objective-C
)的異常處理不同的是是晨,Swift
中的錯誤處理不涉及解除調(diào)用棧肚菠,這是一個計算代價高昂的過程。就此而言署鸡,throw
語句的性能特性可以和return
語句媲美案糙。
- 為了表示一個函數(shù)、方法或構造器可以拋出錯誤靴庆,在函數(shù)聲明后加上
throws
關鍵字。一個標有throws
的函數(shù)稱為throwing
函數(shù)怒医。如果這個函數(shù)指明了返回類型炉抒,throws
關鍵字要寫在返回箭頭->
的前面。只有throwing
函數(shù)可以傳遞錯誤稚叹。任何在非throwing
函數(shù)內(nèi)部拋出的錯誤都只能在函數(shù)內(nèi)部處理焰薄。 -
do-catch
可以對錯誤進行匹配。defer
是將代碼的執(zhí)行延遲到當前作用域退出之前扒袖。
類型轉換
- 類型轉換在
Swift
中使用as
和is
操作符實現(xiàn)塞茅。 - 類型檢查操作符
is
來檢查一個實例是否屬于特定子類型。as?
或者as!
用來轉換季率。 -
Swift
為不確定的類型提供了兩種類型別名:-
Any
標識任何類型野瘦,包括函數(shù)類型。 -
AnyObject
可以表示任何類類型的實例。
-
嵌套類型
- 所謂 嵌套類型鞭光,是指可以在支持的類型中定義嵌套的枚舉吏廉、類和結構體。要在一個類型中嵌套另一個類型惰许,將嵌套類型定義在其外部類型的
{ }
內(nèi)即可席覆,而且根據(jù)需要可以定義多級嵌套。(根據(jù)SwiftLint
不建議嵌套過多)
擴展
- 擴展可以給一個現(xiàn)有類汹买、結構體佩伤、枚舉,還有協(xié)議添加新的功能晦毙。它擁有在不訪問擴展類型源碼實現(xiàn)的基礎上就完成功能擴展的能力生巡,即逆向建模。但需要注意的是结序,擴展可以添加新的功能障斋,但是不能重寫已經(jīng)存在的功能。
Swift
中的擴展可以:- 添加計算型實力屬性和類屬性徐鹤。
- 定義實例方法和類方法垃环。
- 提供新的構造器拷呆。
- 定義下標茎芋。
- 定義和使用新的嵌套類型。
- 使已經(jīng)存在的類型遵循某個協(xié)議塔拳。
- 擴展可以添加新的計算屬性劲赠,但是不能添加存儲屬性涛目,或著向現(xiàn)有屬性添加屬性觀察者。
- 擴展可以添加便利構造器凛澎,但是不能添加指定構造器霹肝。
協(xié)議
- 有時候需要在方法中改變(或異變)方法所屬的實例,對值類型塑煎,則需要加
mutating
關鍵字沫换。如果在協(xié)議中定義了一個實例方法,該方法會改變遵循該協(xié)議的類型的實例最铁,那么在定義協(xié)議時需要在方法前添加mutating
關鍵字讯赏。這使得結構體和枚舉能夠遵循該協(xié)議并滿足此方法要求。 -
協(xié)議合成冷尉,即多個協(xié)議合一通過
&
符號結合起來漱挎。用的并不是很多。 - 協(xié)議擴展雀哨,可以更優(yōu)雅的實現(xiàn)
可選協(xié)議
磕谅。
泛型
請始終以大寫字母開頭的駝峰命名法來為類型參數(shù)命名,以表明它是一個占位類型,而不是一個值怜庸。
-
類型約束指定類型參數(shù)必須繼承自指定類当犯、遵循特定的協(xié)議或協(xié)議組合。如下:
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { // 這里是泛型函數(shù)的函數(shù)體部分 }
-
關聯(lián)類型
定義一個協(xié)議時割疾,聲明一個或多個關聯(lián)類型作為協(xié)議定義的一部分將會非常有用嚎卫。關聯(lián)類型為協(xié)議中某個類型提供了一個占位符名稱,其代表的實際類型在協(xié)議被遵循時才會指定宏榕。關聯(lián)類型通過
associatedtype
關鍵字來指定拓诸。protocol Container { associatedtype Item mutating func append(_ item: Item) var count: Int { get } subscript(i: Int) -> Item { get } }
下面用一個結構體來遵循這個協(xié)議:
struct IntStack: Container { // IntStack 的原始實現(xiàn)部分 var items = [Int]() mutating func push(_ item: Int) { items.append(item) } mutating func pop() -> Int { return items.removeLast() } // Container 協(xié)議的實現(xiàn)部分 typealias Item = Int mutating func append(_ item: Int) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> Int { return items[i] } }
或者使用一個泛型
Stack
來遵循該協(xié)議:struct Stack<Element>: Container { // Stack<Element> 的原始實現(xiàn)部分 var items = [Element]() mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } // Container 協(xié)議的實現(xiàn)部分 mutating func append(_ item: Element) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> Element { return items[i] } }
也可以在協(xié)議里給關聯(lián)類型添加約束來要求遵循的類型滿足協(xié)議:
protocol Container { associatedtype Item: Equatable mutating func append(_ item: Item) var count: Int { get } subscript(i: Int) -> Item { get } }
或者在關聯(lián)類型約束里使用協(xié)議:
protocol SuffixableContainer: Container { associatedtype Suffix: SuffixableContainer where Suffix.Item == Item func suffix(_ size: Int) -> Suffix }
-
** 泛型
Where
語句**需要靈活使用
where
進行條件約束即可。
不透明類型
- 雖然使用不透明類型作為返回值麻昼,看起來和返回協(xié)議類型非常相似奠支,但兩者有個主要區(qū)別,就是是否需要保持類型一致性抚芦。一個不透明類型只能對應一種具體的類型倍谜,即便函數(shù)調(diào)用者并不知道是哪一種類型。協(xié)議類型可以對應多個類型叉抡,只要他們都遵循統(tǒng)一協(xié)議尔崔。總得來說褥民,協(xié)議類型更具靈活性季春,底層類型可以存儲更多的值,而不透明類型對這些底層類型有更強的限定性消返。用法如下:
protocol Shape { func draw() -> String; } struct Quare: Shape { var size: Int func draw() -> String { return size.description; } } class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } /// 返回協(xié)議類型 func makeOneQuare() -> Shape { let q1 = Quare(size: 10) print(q1.draw()) return q1; } /// 返回不透明類型 func makeSecondQuare() -> some Shape { let q2 = Quare(size: 10) print(q2.draw()) return q2; } }
- 說一下自己的看法吧:不透明類型是可以被協(xié)議類型替代使用的载弄,這個就好像
weak
、unowned
兩個關鍵詞一樣撵颊,我們有知道區(qū)別的必要性宇攻,但是即使只用weak
,就可以解決循環(huán)引用的問題了倡勇〕吲觯可能真的有只能使用unowned
的情況,如果以后遇到译隘,會在這里補充。
自動引用計數(shù)
- 引用計數(shù)僅僅用于類的實例洛心。結構體和枚舉都是值類型固耘,不是引用類型,也不是通過引用的方式存儲和傳遞词身。
-
弱引用不會對其引用的實例保持強引用厅目,因而不會阻止
ARC
銷毀被引用的實例。這個特性阻止了引用變?yōu)檠h(huán)引用。關鍵字weak
损敷。另外葫笼,當ARC
設置弱引用為nil
時,屬性觀察不會被觸發(fā)拗馒。 - 和弱引用類似路星,無主引用不會牢牢保持引用的實例。和弱引用不同的是诱桂,無主引用在其他實例有相同或更長聲明周期時使用洋丐。關鍵字是
unowned
。無主引用通常都被期望有值挥等。不過ARC
無法再實例被銷毀后將無主引用設為nil
友绝,因為非可選類型的實例不允許被置為nil
。并且肝劲,使用無主引用迁客,必須確保引用始終指向一個未被銷毀的實例。如果在該實例被銷毀后辞槐,訪問該實例的無主引用掷漱,會觸發(fā)運行時崩潰。 - 閉包和類相似催蝗,都是引用類型切威。循環(huán)引用可以通過弱引用或者無主引用來解決。
內(nèi)存安全
1. 內(nèi)存訪問沖突的實質(zhì):
- 內(nèi)存訪問沖突時丙号,要考慮內(nèi)存訪問上下文的三個性質(zhì):訪問是讀還是寫先朦,訪問的時長,以及訪問的地址犬缨。特別是喳魏,沖突會發(fā)生在有兩個訪問符合下列情況時:
- 至少有一個是寫訪問
- 他們的訪問是同一個內(nèi)存地址
- 他們的訪問在時間線上有部分的重疊。
2. Swift
中兩種長期訪問類型:
-
In-Out
參數(shù)的訪問沖突怀薛。 - 結構體
mutating
方法里刺彩。
3. 這個小節(jié)里舉的例子值得回頭再看看。
主要是針對長期內(nèi)存訪問的分析枝恋,和解決方法创倔。
訪問控制
1. 訪問級別
Swift
為代碼中的實體提供了五種不同的訪問級別:
-
open
和public
級別可以讓實體被同一模塊的所有實體訪問。在模塊外也可以通過導入該模塊來訪問原文件里的所有實體焚碌。通常情況下畦攘,使用open
或者public
來定義模塊對外訪問接口。open
只能作用于類或者類的成員十电,它和public
的區(qū)別主要在于open
限定的類或者類的成員能夠在模塊外被繼承和重寫知押。將類的訪問級別顯示指定為open
表明已經(jīng)設計好了類的代碼叹螟,并且充分考慮到了這個類在其他模塊作為父類時的影響。 -
internal
級別讓實體被同一模塊源文件的任何實體訪問台盯,但是不能被模塊外的實體訪問罢绽。通常情況下,某個實體只在應用程序或者框架內(nèi)部使用静盅,可以將其設置為internal
級別良价。同時,這個也是默認訪問級別温亲。 -
fileprivate
限制實體只能在其定義的文件內(nèi)部訪問棚壁。如果功能代碼的實現(xiàn)細節(jié)只需要在文件內(nèi)部訪問時,可以使用fileprivate
來將其隱藏栈虚。 -
private
限制實體只能在其定義的作用域袖外,以及同一文件的extension
中訪問。非同一個源文件的extension
魂务,也是無法訪問的曼验。
以上, open
的訪問級別最高粘姜,限制最少鬓照。private
的訪問級別最低,限制最多孤紧。
2. 訪問級別規(guī)則
Swift
中訪問級別遵循一個原則:實體不能定義在具有更低級別訪問的實體中豺裆。例如:
- 一個
public
的變量,其所在類型的訪問級別不能是internal
号显、fileprivate
或者private
臭猜。因為無法保證變量的類型在使用變量的地方也具有訪問權限。 - 函數(shù)的訪問級別不能高于它的參數(shù)類型和返回類型的訪問級別押蚤。因為這樣就會出現(xiàn)函數(shù)可以在任何地方被訪問蔑歌,而參數(shù)和返回類型不可以的情況。
注意揽碘,即使違反上面的規(guī)則次屠,正常情況下,也不會編譯或者運行錯誤…
高級運算符
開發(fā)中比較少需要使用高級運算符雳刺,這里留著記錄劫灶,具體使用時在參考,點擊標題即可掖桦。
1. 位運算符
-
按位取反運算符
Bitwise NOT Operator
- 按位取反運算符
~
對一個數(shù)值的全部比特位進行取反 - 按位取反運算符是一個前綴運算符浑此,直接放在運算數(shù)之前,并且他們之間不能添加任何空格滞详。
let age = 18 let invertAge = ~age print(invertAge) // -19
- 按位取反運算符
-
按位與運算符
Bitwise AND Operator
- 按位與運算符
&
對兩個數(shù)的比特位進行合并凛俱。它返回一個新的數(shù)。只有當兩個數(shù)的全部對應為都為1
的時候料饥,新數(shù)的對應位才為1
蒲犬。
let one = 10 let sec = 10 let third = 20 let and1 = one & sec let and2 = one & third print(and1, and2) // 10 0
- 按位與運算符
-
按位或運算符
Bitwise OR Operaor
- 按位或運算符
|
可以對兩個數(shù)的比特位進行比較。它返回一個新的數(shù)岸啡,只要兩個數(shù)的對應位中任意一個為1
原叮,新數(shù)的對應位就為1
。
- 按位或運算符
-
按位異或運算符
Bitwise XOR Operator
- 按位異或運算符巡蘸,或稱排外的運算符
^
奋隶,可以對兩個數(shù)的比特位進行比較。它返回一個新的數(shù)悦荒,當兩個數(shù)的對應為不相同時唯欣,新數(shù)的對應位就為1
,對應位相同時搬味,則為0
境氢。
- 按位異或運算符巡蘸,或稱排外的運算符
-
按位左移、按位右移運算符
Bitwise Left Right Shift Operators
- 按位左移運算符
<<
和按位右移運算符>>
可以對一個數(shù)的所有位指定位數(shù)的左移或者右移碰纬,但是需要遵循下面定義的規(guī)則萍聊。 - 對一個數(shù)進行按位左移或者按位右移,相當于對這個數(shù)進行乘以
2
或者除以2
的運算悦析。將一個整數(shù)左移一位寿桨,等價于將這個數(shù)乘以2
。同樣的强戴,將一個整數(shù)右移一位亭螟,等價于將這個數(shù)除以2
。 - 無符號整數(shù)的移位運算(簡單)
- 有符號整數(shù)的移位運算(復雜)
- 按位左移運算符
2. 溢出運算符
- 溢出加法
&+
- 溢出減法
&-
- 溢出乘法
&*
3. 優(yōu)先級和結合性
運算時酌泰,加上必要的括號媒佣,更具可讀性。
4. 運算符函數(shù)
類和結構體可以為現(xiàn)有的運算符提供自定義的實現(xiàn)陵刹,這通常被稱為 運算符重載
默伍。
- 前綴和后綴運算符,如
-a
衰琐、b!
- 符合賦值運算符也糊,如
=
、+=
- 等價運算符羡宙,如
==
狸剃、!=
5. 自定義運算符
自定義運算符的優(yōu)先級。
后記
本來是想一星期補完的狗热, 結果斷斷續(xù)續(xù)持續(xù)了兩周多钞馁。這里僅僅是記錄自己日常中容易忽視的虑省,或者不太常用的一些點。語言參考只是讓我們對這個語言有個大概的了解僧凰,想要加深對 Swift
的理解探颈,還是應該更多的去看優(yōu)秀的源碼,自己動手去實踐和總結训措。