-
常量與變量
- 使用let來聲明常量,使用var來聲明變量。
- 聲明的同時賦值的話,編譯器會自動推斷類型。
- 值永遠不會被隱式轉換為其他類型。如果你需要把一個值轉換成其他類型,請顯式轉換。
- 如果你需要使用與Swift保留關鍵字相同的名稱作為常量或者變量名,你可以使用反引號(`)將關鍵字包圍的方式將其作為名字使用。無論如何,你應當避免使用關鍵字作為常量或變量名,除非你別無選擇。
-
Swift 包含了 C 和 Objective-C 上所有基礎數據類型,
-
Int表示整型值;Double和Float表示浮點型值;
- 數值類字面量可以包括額外的格式來增強可讀性。整數和浮點數都可以添加額外的零并且包含下劃線,并不會影響字面量:
let justOverOneMillion = 1_000_000.000_000_1
- 數值類字面量可以包括額外的格式來增強可讀性。整數和浮點數都可以添加額外的零并且包含下劃線,并不會影響字面量:
Bool是布爾型值; 有兩個布爾常量,true和false
Character和String是單字符和字符串文本型數據。
三個基本的集合類型,Array,Set和Dictionary
增加了 Objective-C 中沒有的高階數據類型比如元組(Tuple): 把多個值組合成一個復合值。元組內的值可以是任意類型,并不要求是相同類型。
-
增加了可選(Optional)類型,用于處理值缺失的情況。
- 衍生的還有一個隱式解析可選類型
增加了函數類型
-
Swift 的多行注釋可以嵌套在其它的多行注釋之中。
可以使用typealias關鍵字來定義類型別名:typealias AudioSample = UInt16
可選值的強制解析(forced unwrapping):當你確定可選類型確實包含值之后,你可以在可選的名字后面加一個感嘆號(!)來獲取值。這個驚嘆號表示“我知道這個可選有值,請使用它?!?/p>
-
可選綁定:
- 可以用在if和while語句中,這條語句不僅可以用來判斷可選類型中是否有值,同時可以將可選類型中的值賦給一個常量或者變量。if let/var firstNumber = Int(“4”){…}
- 可以包含多個可選綁定在if語句中,并使用where子句做布爾值判斷。if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber < secondNumber { }
-
隱式解析可選類型:
- 類型的可選狀態被定義為隱式解析可選類型(implicitly unwrapped optionals)。把想要用作可選的類型的后面的問號(String?)改成感嘆號(String!)來聲明一個隱式解析可選類型。當可選類型被第一次賦值之后就可以確定之后一直有值的時候,隱式解析可選類型非常有用。隱式解析可選類型主要被用在 Swift 中類的構造過程中
- 一個隱式解析可選類型其實就是一個普通的可選類型,但是可以被當做非可選類型來使用,并不需要每次都使用解析來獲取可選值。
- 如果你在隱式解析可選類型沒有值的時候嘗試取值,會觸發運行時錯誤。和你在沒有值的普通可選類型后面加一個驚嘆號一樣。
- 仍然可以把隱式解析可選類型當做普通可選類型來判斷它是否包含值,也可以在可選綁定中使用隱式解析可選類型來檢查并解析它的值
- 如果一個變量之后可能變成nil的話請不要使用隱式解析可選類型。如果你需要在變量的生命周期中判斷是否是nil的話,請使用普通可選類型。
一個函數可以通過在聲明中添加throws關鍵詞來聲明可能拋出錯誤消息,throw關鍵字用來拋出錯誤。當你使用的函數可能拋出錯誤消息時, 你應該在表達式中前置try關鍵詞。
-
assert斷言:(發布版本中自動失效)
- let age = -3; assert(age >= 0, "A person's age cannot be less than zero”);
- // 因為 age < 0,所以斷言會觸發
- //條件為true時才會向下執行,否則中斷程序,輸出后邊的字串信息,但后邊的信息是可選的
-
基本運算符
賦值符(=)不返回值,以防止把想要判斷相等運算符(==)的地方寫成賦值符導致的錯誤。
區別于 C 語言,在 Swift 中你可以對浮點數進行取余運算(%),
提供了 C 語言沒有的表達兩數之間的值的區間運算符(a..<b和a...b),這方便我們表達一個區間內的數值。
與 C 語言和 Objective-C 不同的是,Swift 默認情況下不允許在數值運算中出現溢出情況。但是你可以使用 Swift 的溢出運算符來實現溢出運算(&+ , &-, &*在2.0語法中&/和&%被移除)。
Swift 也提供恒等===和不恒等!==這兩個比較符來判斷兩個對象是否引用同一個對象實例。
-
空合運算符(a ?? b)將對可選類型a進行空判斷,如果a包含一個值就進行解封,否則就返回一個默認值b.這個運算符有兩個條件
- 表達式a必須是Optional類型 ·
- 默認值b的類型必須要和a存儲值的類型保持一致
- 如果a為非空值(non-nil),那么值b將不會被估值。這也就是所謂的短路求值。
-
字符串和字符
- String是例如"hello, world","albatross"這樣的有序的Character(字符)類型的值的集合。
- 與OC的NSString不同的時String類型是值類型。
- 在實際編譯時,Swift 編譯器會優化字符串的使用,使實際的復制只發生在絕對必要的情況下,這意味著您將字符串作為值類型的同時可以獲得極高的性能。
- 使用startIndex屬性可以獲取一個String的第一個Character的索引。使用endIndex屬性可以獲取最后一個Character的后一個位置的索引。因此,endIndex屬性不能作為一個字符串的有效下標。如果String是空串,startIndex和endIndex是相等的。
- 通過調用String.Index的predecessor()方法,可以立即得到前面一個索引,調用successor()方法可以立即得到后面一個索引。任何一個String的索引都可以通過鎖鏈作用的這些方法來獲取另一個索引,也可以調用advancedBy(_:x)方法來獲取代表其后第x的index。但如果嘗試獲取出界的字符串索引,就會拋出一個運行時錯誤。
- 使用characters屬性的indices屬性會創建一個包含全部索引的范圍(Range),用來在一個字符串中訪問單個字符。
-
集合類型
- Swift 語言中的Arrays、Sets和Dictionaries中存儲的數據值類型必須明確。這意味著我們不能把不正確的數據類型插入其中。
- 如果我們同時需要每個數據項的值和索引值,可以使用enumerate()方法來進行數組遍歷。enumerate()返回一個由每一個數據項索引值和數據值組成的元組。我們可以把這個元組分解成臨時常量或者變量來進行遍歷:for (index, value) in shoppingList.enumerate( ) { }
-
控制流
-
switch
case分支必須包含所有可能性
不存在隱式的貫穿(No Implicit Fallthrough),可以使用Fallthrough實現顯示貫穿
一個 case 也可以包含多個模式,用逗號把它們分開(如果太長了也可以分行寫)
case 分支的模式也可以是一個值的區間.
不像 C 語言,Swift 允許多個 case 匹配同一個值??梢允褂迷M在同一個switch語句中測試多個值。元組中的元素可以是值,也可以是區間。另外,使用下劃線(_)來匹配所有可能的值。
可以進行值綁定(value binding):case 分支的模式允許將匹配的值綁定到一個臨時的常量或變量,這些常量或變量在該 case 分支里就可以被引用了.case 分支的模式可以使用where語句來判斷額外的條件。
-
標簽語句:
- 產生一個帶標簽的語句是通過在該語句的關鍵詞的同一行前面放置一個標簽,并且該標簽后面還需帶著一個冒號。然后可以使用break或continue加上標簽來顯式指明作用循環或switch.
-
guard語句:
- 像if語句一樣,guard的執行取決于一個表達式的布爾值。我們可以使用guard語句來要求條件必須為真時,以執行guard語句后的代碼。不同于if語句,一個guard語句總是有一個else分句,如果條件不為真則在else分支上的代碼就會被執行。這個分支必須轉移控制以退出guard語句出現的代碼段。它可以用控制轉移語句如return,break或continue做這件事,或者它調用了一個不返回的方法或函數,例如fatalError()
-
檢測API是否可用
- Swift 有內置支持去檢查接口的可用性的,這可以確保我們不會不小心地使用對于當前部署目標不可用的API。
-
available(iOS 9, OSX 10.10, *) 返回bool值
-
-
函數(Functions)
-
在 Swift 中,每個函數都有一種類型,包括函數的參數值類型和返回值類型。
- 可以把函數類型當做任何其他普通變量類型一樣處理,這樣就可以更簡單地把函數當做別的函數的參數,也可以從其他函數中返回函數。
函數的定義可以寫在在其他函數定義中,這樣可以在嵌套函數范圍內實現功能封裝。
如果函數返回的元組類型中有可能在整個元組中含有“沒有值”,你可以使用可選的(Optional) 元組(元組類型的右括號后放置一個問號來定義一個可選元組)返回類型反映整個元組可以是nil的事實。
-
參數
-
函數參數都有一個外部參數名(external parameter name)和一個本地參數名(local parameter name)。外部參數名用來標記傳遞給函數調用的參數,本地參數名在實現函數的時候使用。
- 你可以在本地參數名前指定外部參數名,中間以空格分隔。注意: 如果你提供了外部參數名,那么函數在被調用時,必須使用外部參數名。
- 如果你不想為第二個及后續的參數設置參數名,用一個下劃線(_)代替一個明確地參數名。注意: 因為第一個參數默認忽略其外部參數名稱,明確寫下劃線是多余的。
可以使用默認參數.注意: 將帶有默認值的參數放在函數參數列表的最后。這樣可以保證在函數調用時,非默認參數的順序是一致的,同時使得相同的函數在不同情況下調用時顯得更為清晰。
-
可變參數
- 可變參數的傳入值在函數體為此類型的一個數組。例如,一個叫做 numbers 的 Double... 型可變參數,在函數體內可以當做一個叫 numbers 的 [Double] 型的數組常量。
- 注意: 一個函數最多只能有一個可變參數,而且可變參數放在參數表的最后.
- 函數參數默認是常量,可以通過在參數名前加關鍵字 var 來定義變量參數.
- 注意: 對變量參數所進行的修改在函數調用結束后便消失了,并且對于函數體外是不可見的。變量參數僅僅存在于函數調用的生命周期中。
-
輸入輸出inout參數
- 定義一個輸入輸出參數時,在參數定義前加 inout 關鍵字。一個輸入輸出參數在函數內部被修改,然后被傳出函數,替換原來的值。
- 只能將變量作為輸入輸出參數.傳入的參數作為輸入輸出參數時,需要在參數前加&符,表示這個值可以被函數修改。
- 注意: 輸入輸出參數不能有默認值,而且可變參數不能用 inout 標記。如果你用 inout 標記一個參數,這個參數不能被 var 或者 let 標記。
- 注意: 輸入輸出參數和返回值是不一樣的。輸入輸出參數是函數對函數體外產生影響的另一種方式。
-
-
-
函閉包( Closures)
閉包可以捕獲和存儲其所在上下文中任意常量和變量的引用。 這就是所謂的閉合并包裹著這些常量和變量,俗稱閉包。Swift 會為您管理在捕獲過程中涉及到的所有內存操作。
閉包和函數都是引用類型.
-
閉包采取如下三種形式之一
- 全局函數是一個有名字但不會捕獲任何值的閉包 ·
- 嵌套函數是一個有名字并可以捕獲其封閉函數域內值的閉包 ·
- 閉包表達式是一個利用輕量級語法所寫的可以捕獲其上下文中變量或常量值的匿名閉包
-
Swift 的閉包表達式擁有簡潔的風格,并鼓勵在常見場景中進行語法優化,主要優化如下:
- 利用上下文推斷參數和返回值類型 ·
- 隱式返回單表達式閉包,即單表達式閉包可以省略return關鍵字 ·
- 參數名稱縮寫 ·
- 尾隨(Trailing)閉包語法
-
提供閉包函數
- 一種方式是撰寫一個符合其類型要求的普通函數,并將其作為方法的參數傳入
- 另一種是閉包表達式: 可以使用常量、變量和inout類型作為參數,不提供默認值。 也可以在參數列表的最后使用可變參數。 元組也可以作為參數和返回值。{ (parameters) -> returnType in statements }
所有的類型都可以被正確推斷,則返回箭頭 (->) 和圍繞在參數周圍的括號也可以被省略
單行表達式閉包可以通過隱藏return關鍵字來隱式返回單行表達式的結果
Swift 自動為內聯函數提供了參數名稱縮寫功能,您可以直接通過$0,$1,$2來順序調用閉包的參數。
如果您在閉包表達式中使用參數名稱縮寫,您可以在閉包參數列表中省略對其的定義,并且對應參數名稱縮寫的類型會通過函數類型進行推斷。 in關鍵字也同樣可以被省略.
-
如果您需要將一個很長的閉包表達式作為最后一個參數傳遞給函數,可以使用尾隨閉包來增強函數的可讀性。 尾隨閉包是一個書寫在函數括號之后的閉包表達式,函數支持將其作為最后一個參數調用。
- 注意: 如果函數只需要閉包表達式一個參數,當您使用尾隨閉包時,您甚至可以把()省略掉。
-
捕獲值
- 閉包可以在其定義的上下文中捕獲常量或變量。 即使定義這些常量和變量的原域已經不存在,閉包仍然可以在閉包函數體內引用和修改這些值。
- Swift最簡單的閉包形式是嵌套函數,也就是定義在其他函數的函數體內的函數。 嵌套函數可以捕獲其外部函數所有的參數以及定義的常量和變量。
- 原函數每次返回嵌套函數作為返回值給變量,則兩個不同的變量擁有不同的閉包空間,兩者互不影響.
-
枚舉(Enumerations)
在 C 語言中枚舉將枚舉名和一個整型值相對應。Swift 中的枚舉更加靈活,不必給每一個枚舉成員提供一個值。如果給枚舉成員提供一個值(稱為“原始”值),則該值的類型可以是字符串,字符,或是一個整型值或浮點數。
-
在 Swift 中,枚舉類型是一等公民(first-class)。它們采用了很多傳統上只被類(class)所支持的特征,例如
- 計算型屬性(computed properties),用于提供關于枚舉當前值的附加信息,
- 實例方法(instance methods),用于提供和枚舉所代表的值相關聯的功能。
- 枚舉也可以定義構造函數(initializers)來提供一個初始值;
- 可以在原始的實現基礎上擴展它們的功能;
- 可以遵守協議(protocols)來提供標準的功能。
和 C 和 Objective-C 不同,Swift 的枚舉成員在被創建時不會被賦予一個默認的整型值。
變量枚舉類型已知時,再次為其賦值可以省略枚舉名。
在判斷一個枚舉類型的值時,switch語句必須窮舉所有情況,否則將無法通過編譯.強制性全部窮舉的要求確保了枚舉成員不會被意外遺漏。
-
相關值
可以使用相關值:讓你存儲成員值之外的自定義信息,并且當你每次在代碼中使用該成員時允許這個信息產生變化。
-
可以在switch的 case 分支代碼中提取每個相關值作為一個常量(用let前綴)或者作為一個變量(用var前綴)來使用
- 如果一個枚舉成員的所有相關值被提取為常量,或者它們全部被提取為變量,為了簡潔,你可以只放置一個var或者let標注在緊跟case后,成員名稱前
-
原始值
作為相關值的另一種選擇,枚舉成員可以被默認值(稱為原始值)賦值,其中這些原始值具有相同的類型。
原始值可以是字符串,字符,或者任何整型值或浮點型值。每個原始值在它的枚舉聲明中必須是唯一的。
-
原始值的隱式賦值:在使用原始值為整數或者字符串類型的枚舉時,不需要顯式的為每一個成員賦值,這時,Swift將會自動為你賦值。
- 當使用整數作為原始值時,隱式賦值的值依次遞增1。如果第一個值沒有被賦初值,將會被自動置為0。
- 當使用字符串作為枚舉類型的初值時,每個枚舉成員的隱式初值則為該成員的名稱。
如果在定義枚舉類型的時候使用了原始值,那么將會自動獲得一個初始化方法,這個方法將原始值類型作為參數,返回枚舉成員或者nil。你可以使用這種初始化方法來創建一個新的枚舉變量。 let possiblePlanet = Planet(rawValue: 7)
使用枚舉成員的rawValue屬性可以訪問該枚舉成員的原始值
-
原始值與枚舉值
- 原始值和相關值是不相同的。
- 當你開始在你的代碼中定義枚舉的時候原始值是被預先填充的值。對于一個特定的枚舉成員,它的原始值始終是相同的。
- 相關值是當你在創建一個基于枚舉成員的新常量或變量時才會被設置,并且每次當你這么做得時候,它的值可以是不同的。
-
遞歸枚舉
- 遞歸枚舉(recursive enumeration)是一種枚舉類型,表示它的枚舉中,有一個或多個枚舉成員擁有該枚舉的其他成員作為相關值。使用遞歸枚舉時,編譯器會插入一個中間層。
- 你可以在枚舉成員前加上indirect來表示這成員可遞歸。也可以在枚舉類型開頭加上indirect關鍵字來表示它的所有成員都是可遞歸的.
-
類和結構體(Classes and Structures)
在一個單一文件中定義一個類或者結構體,系統將會自動生成面向其它代碼的外部接口。
-
命名
- 使用 UpperCamelCase 這種方式來命名(如 SomeClass 和SomeStructure等),以便符合標準Swift 類型的大寫命名風格(如String,Int和Bool)。
- 相反的,請使用lowerCamelCase這種方式為屬性和方法命名(如framerate和incrementCount),以便和類區分。
通過使用點語法(dot syntax),你可以訪問實例中所含有的屬性。
所有結構體都有一個自動生成的成員逐一構造器,用于初始化新結構體實例中成員的屬性。與結構體不同,類實例沒有默認的成員逐一構造器。
Swift 中字符串(String),數組(Array)和字典(Dictionary)類型均以結構體的形式實現.是值類型.
-
Objective-C中字符串(NSString),數組(NSArray)和字典(NSDictionary)類型均以類的形式實現,是引用類型.
- 實際上,在 Swift 中,所有的基本類型都是值類型,并且都是以結構體的形式在后臺所實現。
-
Swift 中類和結構體有很多共同點與不同點:
-
共同點
- 定義屬性用于存儲值
- 定義方法用于提供功能
- 定義附屬腳本用于訪問值
- 定義構造器用于生成初始化值
- 通過擴展以增加默認實現的功能
- 實現協議以提供某種標準功能
-
不同點
- 繼承允許一個類繼承另一個類的特征
- 類型轉換允許在運行時檢查和解釋一個類實例的類型
- 解構器允許一個類實例釋放任何其所被分配的資源
- 引用計數允許對一個類的多次引用
- 類是引用類型,結構體是值類型
-
-
屬性 (Properties)
-
屬性將值跟特定的類、結構或枚舉關聯。
存儲屬性存儲常量或變量作為實例的一部分,,存儲屬性只能用于類和結構體。可以在定義存儲屬性的時候指定默認值
-
計算屬性計算(不是存儲)一個值。計算屬性可以用于類、結構體和枚舉.
- 注意:必須使用var關鍵字定義計算屬性,包括只讀計算屬性,因為它們的值不是固定的。let關鍵字只用來聲明常量屬性,表示初始化后再也無法修改的值。
存儲屬性和計算屬性通常與特定類型的實例關聯。但是,屬性也可以直接作用于類型本身,這種屬性稱為類型屬性。
當值類型的實例被聲明為常量的時候,它的所有屬性也就成了常量,即使在定義的時候是變量.
-
延遲存儲屬性是指當第一次被調用的時候才會計算其初始值的屬性。
- 在屬性聲明前使用lazy來標示一個延遲存儲屬性。
- 注意:必須將延遲存儲屬性聲明成變量(使用var關鍵字),因為屬性的初始值可能在實例構造完成之后才會得到。而常量屬性在構造過程完成之前必須要有初始值,因此無法聲明成延遲屬性。
- 注意:如果一個被標記為lazy的屬性在沒有初始化時就同時被多個線程訪問,則無法保證該屬性只會被初始化一次。
-
屬性觀察器監控和響應屬性值的變化,每次屬性被設置值的時候都會調用屬性觀察器,甚至新的值和現在的值相同的時候也不例外。
- 可以為除了延遲存儲屬性之外的其他存儲屬性添加屬性觀察器,也可以通過重載屬性的方式為繼承的屬性(包括存儲屬性和計算屬性)添加屬性觀察器
- 注意:不需要為非重載的計算屬性添加屬性觀察器,因為可以通過它的 setter 直接監控和響應值的變化。
- 注意:如果在一個屬性的didSet觀察器里為它賦值,這個值會替換該觀察器之前設置的值。
-
全局與局部變量
全局或局部變量都屬于存儲型變量,跟存儲屬性類似,它提供特定類型的存儲空間,并允許讀取和寫入。
-
在全局或局部范圍都可以定義計算型變量和為存儲型變量定義觀察器。計算型變量跟計算屬性一樣,返回一個計算的值而不是存儲值,聲明格式也完全一樣。
- 注意:全局的常量或變量都是延遲計算的,跟延遲存儲屬性相似,不同的地方在于,全局的常量或變量不需要標記lazy特性。
- 注意:局部范圍的常量或變量不會延遲計算。
-
類型屬性用于定義特定類型所有實例共享的數據,
使用關鍵字static來定義類型屬性。在為類(class)定義計算型類型屬性時,可以使用關鍵字class來支持子類對父類的實現進行重寫。
-
值類型的存儲型類型屬性可以是變量或常量
- 注意:跟實例的存儲屬性不同,必須給存儲型類型屬性指定默認值,因為類型本身無法在初始化過程中使用構造器給類型屬性賦值。
- 注意:存儲型類型屬性是延遲初始化的(lazily initialized),它們只有在第一次被訪問的時候才會被初始化。即使它們被多個線程同時訪問,系統也保證只會對其進行初始化一次,并且不需要對其使用 lazy 修飾符。
- 計算型類型屬性跟實例的計算屬性一樣只能定義成變量屬性。
-
-
方法(Methods)
-
方法是與某些特定類型相關聯的函數。
- 實例方法:類、結構體、枚舉都可以定義實例方法;實例方法為給定類型的實例封裝了具體的任務與功能
- 類型方法:類、結構體、枚舉也可以定義類型方法;類型方法與類型本身相關聯。類型方法與 Objective-C 中的類方法(class methods)相似。
結構體和枚舉能夠定義方法是 Swift 與 C/Objective-C 的主要區別之一。在 Objective-C 中,類是唯一能定義方法的類型。但在 Swift 中,你不僅能選擇是否要定義一個類/結構體/枚舉,還能靈活的在你創建的類型(類/結構體/枚舉)上定義方法。
Swift 默認僅給方法的第一個參數名稱一個局部參數名稱;默認同時給第二個和后續的參數名稱局部參數名稱和外部參數名稱。這個約定與典型的命名和調用約定相適應,與你在寫 Objective-C 的方法時很相似。
-
變異(mutating)方法:
-
結構體和枚舉是值類型。一般情況下,值類型的屬性不能在它的實例方法中被修改。但是,如果你確實需要在某個具體的方法中修改結構體或者枚舉的屬性,你可以選擇變異(mutating)這個方法,然后方法就可以從方法內部改變它的屬性;要使用變異方法, 將關鍵字mutating 放到方法的func關鍵字之前就可以了.
- 注意:不能在結構體類型常量上調用變異方法,因為常量的屬性不能被改變,即使想改變的是常量的變量屬性也不行.
變異方法能夠賦給隱含屬性self一個全新的實例
-
-
-
下標腳本(Subscripts)
-
下標腳本
- 可以定義在類(Class)、結構體(structure)和枚舉(enumeration)這些目標中,可以認為是訪問集合(collection),列表(list)或序列(sequence的快捷方式,使用下標腳本的索引設置和獲取值,不需要再調用實例的特定的賦值和訪問方法。
- 與定義實例方法類似,定義下標腳本使用subscript關鍵字,顯式聲明入參(一個或多個)和返回類型。與實例方法不同的是下標腳本可以設定為讀寫或只讀。
-
下標腳本選項
- 下標腳本允許任意數量的入參索引,并且每個入參類型也沒有限制。
- 下標腳本的返回值也可以是任何類型。
- 下標腳本可以使用變量參數和可變參數,但不允許使用寫入讀出(in-out)參數或給參數設置默認值。
-
下標腳本的重載
- 一個類或結構體可以根據自身需要提供多個下標腳本實現,在定義下標腳本時通過入參的類型進行區分,使用下標腳本時會自動匹配合適的下標腳本實現運行。
-
-
繼承(Inheritance)
-
重寫(overriding)
- 子類可以為繼承來的實例方法(instance method),類方法(class method),實例屬性(instance property),或下標腳本(subscript)提供自己定制的實現(implementation)。
- 重寫某個特性,你需要在重寫定義的前面加上override關鍵字。這么做,你就表明了你是想提供一個重寫版本,而非錯誤地提供了一個相同的定義。意外的重寫行為可能會導致不可預知的錯誤,任何缺少override關鍵字的重寫都會在編譯時被診斷為錯誤。
- override關鍵字會提醒 Swift 編譯器去檢查該類的超類(或其中一個父類)是否有匹配重寫版本的聲明。這個檢查可以確保你的重寫定義是正確的。
重寫方法:可以重寫繼承來的實例方法或類方法,提供一個定制或替代的方法實現。
-
重寫屬性
-
重寫屬性的Getters和Setters
- 可以提供定制的 getter(或 setter)來重寫任意繼承來的屬性,無論繼承來的屬性是存儲型的還是計算型的屬性。
- 子類并不知道繼承來的屬性是存儲型的還是計算型的,它只知道繼承來的屬性會有一個名字和類型。
- 你在重寫一個屬性時,必需將它的名字和類型都寫出來。這樣才能使編譯器去檢查你重寫的屬性是與超類中同名同類型的屬性相匹配的。
- 可以將一個繼承來的只讀屬性重寫為一個讀寫屬性,只需要你在重寫版本的屬性里提供 getter 和 setter 即可。
- 但是,你不可以將一個繼承來的讀寫屬性重寫為一個只讀屬性。
-
重寫屬性觀察器(Property Observer)
- 可以在屬性重寫中為一個繼承來的屬性添加屬性觀察器。這樣一來,當繼承來的屬性值發生改變時,你就會被通知到,無論那個屬性原本是如何實現的。
- 注意:你不可以為繼承來的常量存儲型屬性或繼承來的只讀計算型屬性添加屬性觀察器。這些屬性的值是不可以被設置的,所以,為它們提供willSet或didSet實現是不恰當。
-
-
防止重寫
- 可以通過把類, 方法,屬性或下標腳本標記為final來防止它們被重寫,只需要在聲明關鍵字前加上final特性即可。
-
-
構造過程(Initialization)
與 Objective-C 中的構造器不同,Swift 的構造器無需返回值,它們的主要任務是保證新實例在第一次使用前完成正確的初始化。
-
類和結構體在實例創建時,必須為所有存儲型屬性設置合適的初始值。存儲型屬性的值不能處于一個未知的狀態。
- 注意:當你為存儲型屬性設置默認值或者在構造器中為其賦值時,它們的值是被直接設置的,不會觸發任何屬性觀測器(property observers)。
- 可以在構造器中為存儲型屬性設置初始值。同樣,你也可以在屬性聲明時為其設置默認值。
- 構造函數中可以修改常量值:只要在構造過程結束前常量的值能確定,你可以在構造過程中的任意時間點修改常量屬性的值。
-
默認構造器與逐一成員構造器:
- Swift 將為所有屬性已提供默認值的且自身沒有定義任何構造器的結構體或基類,提供一個默認的構造器。這個默認構造器將簡單的創建一個所有屬性值都設置為默認值的實例。
- 結構體對所有存儲型屬性提供了默認值且自身沒有提供定制的構造器,它們能自動獲得一個逐一成員構造器。
-
值類型的構造器代理:
-
構造器可以通過調用其它構造器來完成實例的部分構造過程。這一過程稱為構造器代理,它能減少多個構造器間的代碼重復。
-
值類型(結構體和枚舉類型)不支持繼承,所以構造器代理的過程相對簡單,因為它們只能代理給本身提供的其它構造器。
- 值類型,你可以使用self.init在自定義的構造器中引用其它的屬于相同值類型的構造器。并且你只能在構造器內部調用self.init。
- 如果你為某個值類型定義了一個定制的構造器,你將無法訪問到默認構造器(如果是結構體,則無法訪問逐一對象構造器)。這個限制可以防止你在為值類型定義了一個更復雜的,完成了重要準備構造器之后,別人還是錯誤的使用了那個自動生成的構造器。
類則不同,它可以繼承自其它類,這意味著類有責任保證其所有繼承的存儲型屬性在構造時也能正確的初始化。
-
-
-
類的繼承和構造過程
-
Swift 提供了兩種類型的類構造器來確保所有類實例中存儲型屬性都能獲得初始值,它們分別是指定構造器和便利構造器。
-
指定構造器:
- 指定構造器是類中最主要的構造器。一個指定構造器將初始化類中提供的所有屬性,并根據父類鏈往上調用父類的構造器來實現父類的初始化。
-
便利構造器:
- 便利構造器是類中比較次要的、輔助型的構造器。你可以定義便利構造器來調用同一個類中的指定構造器,并為其參數提供默認值。你也可以定義便利構造器來創建一個特殊用途或特定輸入的實例。
- 需要在init關鍵字之前放置convenience關鍵字,并使用空格將它們倆分開
-
-
-
類的構造器代理規則
-
為了簡化指定構造器和便利構造器之間的調用關系,Swift 采用以下三條規則來限制構造器之間的代理調用:
規則 1-指定構造器必須調用其直接父類的的指定構造器。
規則 2-便利構造器必須調用同一類中定義的其它構造器。
規則 3-便利構造器必須最終以調用一個指定構造器結束。
-
一個更方便記憶的方法是:
- 指定構造器必須總是向上代理
- 便利構造器必須總是橫向代理
-
-
Swift 編譯器將執行 4 種有效的安全檢查,以確保兩段式構造過程能順利完成:
- 安全檢查 1-指定構造器必須保證它所在類引入的所有屬性都必須先初始化完成,之后才能將其它構造任務向上代理給父類中的構造器。
- 安全檢查 2-指定構造器必須先向上代理調用父類構造器,然后再為繼承的屬性設置新值。如果沒這么做,指定構造器賦予的新值將被父類中的構造器所覆蓋。
- 安全檢查 3-便利構造器必須先代理調用同一類中的其它構造器,然后再為任意屬性賦新值。如果沒這么做,便利構造器賦予的新值將被同一類中其它指定構造器所覆蓋。
- 安全檢查 4-構造器在第一階段構造完成之前,不能調用任何實例方法、不能讀取任何實例屬性的值,self的值不能被引用。
-
跟 Objective-C 中的子類不同,Swift 中的子類不會默認繼承父類的構造器,但可以被重寫。Swift 的這種機制可以防止一個父類的簡單構造器被一個更專業的子類繼承,并被錯誤的用來創建子類的實例。
-
子類不會默認繼承父類的構造器。但是如果特定條件可以滿足,父類構造器是可以被自動繼承的。
- 規則 1-如果子類沒有定義任何指定構造器,它將自動繼承所有父類的指定構造器。
- 規則 2-如果子類提供了所有父類指定構造器的實現--不管是通過規則1繼承過來的,還是通過自定義實現的--它將自動繼承所有父類的便利構造器。
-
-
可失敗構造器:
- 為了妥善處理這種構造過程中可能會失敗的情況。你可以在一個類,結構體或是枚舉類型的定義中,添加一個或多個可失敗構造器。其語法為在init關鍵字后面加添問號(init?)。
- 你也可以使用通過在init后面添加驚嘆號的方式來定義一個可失敗構造器(init!),該可失敗構造器將會構建一個特定類型的隱式解析可選類型的對象。
-
必要構造器
- 在類的構造器前添加 required 修飾符表明所有該類的子類都必須實現該構造器
- 注意:如果子類繼承的構造器能滿足必要構造器的需求,則你無需顯示的在子類中提供必要構造器的實現。
-
通過閉包和函數來設置屬性的默認值
- 如果某個存儲型屬性的默認值需要特別的定制或準備,你就可以使用閉包或全局函數來為其屬性提供定制的默認值。每當某個屬性所屬的新類型實例創建時,對應的閉包或函數會被調用,而它們的返回值會當做默認值賦值給這個屬性。
-
析構過程(Deinitialization)
- 析構器是在實例釋放發生前被自動調用。析構器是不允許被主動調用的。
- 子類繼承了父類的析構器,并且在子類析構器實現的最后,父類的析構器會被自動調用。
- 即使子類沒有提供自己的析構器,父類的析構器也同樣會被調用。
-
自動引用計數(Automatic Reference Counting)
-
類實例之間的循環強引用
-
你可以通過定義類之間的關系為弱引用或無主引用,以替代強引用,從而解決循環強引用的問題。弱引用和無主引用允許循環引用中的一個實例引用另外一個實例而不保持強引用。這樣實例能夠互相引用而不產生循環強引用。
-
對于生命周期中會變為nil的實例使用弱引用。
- 聲明屬性或者變量時,在前面加上weak關鍵字表明這是一個弱引用。
-
對于初始化賦值后再也不會被賦值為nil的實例,使用無主引用。
- 和弱引用類似,無主引用不會牢牢保持住引用的實例。
- 和弱引用不同的是,無主引用是永遠有值的。因此,無主引用總是被定義為非可選類型(non-optional type)。
- 你可以在聲明屬性或者變量時,在前面加上關鍵字unowned表示這是一個無主引用。
-
-
-
相互強引用的場景及處理辦法
- 兩個屬性的值都允許為nil,并會潛在的產生循環強引用。這種場景最適合用弱引用來解決。
- 一個屬性的值允許為nil,而另一個屬性的值不允許為nil,這也可能會產生循環強引用。這種場景最適合通過無主引用來解決。
- 兩個屬性都必須有值,并且初始化完成后永遠不會為nil。在這種場景中,需要一個類使用無主屬性,而另外一個類使用隱式解析可選屬性。
-
閉包引起的循環強引用
問題的產生:循環強引用還會發生在當你將一個閉包賦值給類實例的某個屬性,并且這個閉包體中又使用了這個類實例。
-
解決辦法:在定義閉包時同時定義捕獲列表作為閉包的一部分,通過這種方式可以解決閉包和類實例之間的循環強引用。捕獲列表定義了閉包體內捕獲一個或者多個引用類型的規則。跟解決兩個類實例間的循環強引用一樣,聲明每個捕獲的引用為弱引用或無主引用,而不是強引用。應當根據代碼關系來決定使用弱引用還是無主引用。
在閉包和捕獲的實例總是互相引用時并且總是同時銷毀時,將閉包內的捕獲定義為無主引用。
-
在被捕獲的引用可能會變為nil時,將閉包內的捕獲定義為弱引用。
- 注意: Swift 有如下要求:只要在閉包內使用self的成員,就要用self.someProperty或者self.someMethod()(而不只是someProperty或someMethod())。這提醒你可能會一不小心就捕獲了self。
-
-
可空鏈式調用(Optional Chaining)
- 通過可空鏈式調用得到的返回值都是可空的。
-
錯誤處理(Error Handling)
在Swift中,錯誤用符合ErrorType協議的值表示。 Swift枚舉特別適合把一系列相關的錯誤組合在一起,同時可以把一些相關的值和錯誤關聯在一起。因此編譯器會為實現ErrorType協議的Swift枚舉類型自動實現相應合成。
-
throws,throw,do,try,catch
通過在函數或方法聲明的參數后面加上throws關鍵字,表明這個函數或方法可以拋出錯誤。如果指定一個返回值,可以把throws關鍵字放在返回箭頭(->)的前面。除非明確地指出,一個函數,方法或者閉包就不能拋出錯誤。
在拋出函數體的任意一個地方,可以通過throw語句拋出錯誤。
-
當調用一個拋出函數的時候,在調用前面加上try。
- 可以通過try!來調用拋出函數或方法禁止錯誤傳送
使用do-catch語句來就捕獲和處理錯誤
類似switch語句,編譯器會檢查catch分句是否能夠處理全部錯誤。
defer語句把執行推遲到退出當前域的時候,。被推遲的操作的執行的順序和他們定義的順序相反.
-
類型轉換(Type Casting)
-
類型轉換在 Swift 中使用 is 和 as 操作符實現。
用類型檢查操作符(is)來檢查一個實例是否屬于特定子類型。
-
因為向下轉型可能會失敗,類型轉型操作符帶有兩種不同形式。
條件形式(conditional form) as? 返回一個你試圖向下轉成的類型的可選值(optional value)。
-
強制形式 as! 把試圖向下轉型和強制解包(force-unwraps)結果作為一個混合動作。
- 注意:轉換沒有真的改變實例或它的值。潛在的根本的實例保持不變;只是簡單地把它作為它被轉換成的類來使用。
- 注意:在一個switch語句的case中使用強制形式的類型轉換操作符(as, 而不是 as?)來檢查和轉換到一個明確的類型。在 switch case 語句的內容中這種檢查總是安全的。
-
Swift為不確定類型提供了兩種特殊類型別名:
AnyObject可以代表任何class類型的實例。
-
Any可以表示任何類型,包括方法類型(function types)。
- 注意:只有當你明確的需要它的行為和功能時才使用Any和AnyObject。在你的代碼里使用你期望的明確的類型總是更好的。
-
-
嵌套類型(Nested Types)
- 要在一個類型中嵌套另一個類型,將需要嵌套的類型的定義寫在被嵌套類型的區域{ }內,而且可以根據需要定義多級嵌套。
- 在外部對嵌套類型的引用,以被嵌套類型的名字為前綴,加上所要引用的屬性名
-
擴展(Extensions)
-
擴展就是向一個已有的類、結構體、枚舉類型或者協議類型添加新功能(functionality)。
- 注意:擴展可以對一個類型添加新的功能,但是不能重寫已有的功能。
- 注意:如果你定義了一個擴展向一個已有類型添加新功能,那么這個新功能對該類型的所有已有實例中都是可用的,即使它們是在你的這個擴展的前面定義的。
-
Swift 中的擴展可以: ·
- 添加計算型屬性和計算型靜態屬性 ·
- 定義實例方法和類型方法 ·
- 提供新的構造器 ·
- 定義下標 ·
- 定義和使用新的嵌套類型 ·
- 使一個已有類型符合某個協議
-
-
協議(Protocols)
-
協議定義了一個藍圖,規定了用來實現某一特定工作或者功能所必需的方法和屬性。
-
屬性:
- 協議可以規定其遵循者提供特定名稱和類型的實例屬性(instance property)或類屬性(type property),而不指定是存儲型屬性(stored property)還是計算型屬性(calculate property)。
- 此外還必須在類型聲明后加上{ set get }來表示屬性是只讀的還是可讀可寫的。
- 定義類屬性(type property)時,總是使用static關鍵字作為前綴。當協議的遵循者是類時,可以使用class或static關鍵字來聲明類屬性,但是在協議的定義中,仍然要使用static關鍵字。
-
方法:
這些方法作為協議的一部分,像普通的方法一樣放在協議的定義中,但是不需要大括號和方法體。
可以在協議中定義具有可變參數的方法,和普通方法的定義方式相同。
但是在協議的方法定義中,不支持參數默認值。
協議中定義類方法的時候,總是使用static關鍵字作為前綴。當協議的遵循者是類的時候,雖然你可以在類的實現中使用class或者static來實現類方法,但是在協議中聲明類方法,仍然要使用static關鍵字。
-
對Mutating方法的規定:
- 值類型(結構體,枚舉)的實例方法中,將mutating關鍵字作為函數的前綴,寫在func之前,表示可以在該方法中修改它所屬的實例及其實例屬性的值。
- 注意:用類實現協議中的mutating方法時,不用寫mutating關鍵字;用結構體,枚舉實現協議中的mutating方法時,必須寫mutating關鍵字。
-
對構造器的規定:
- 可以在遵循該協議的類中實現構造器,并指定其為類的指定構造器(designated initializer)或者便利構造器(convenience initializer)。在這兩種情況下,你都必須給構造器實現標上"required"修飾符.
- 使用required修飾符可以保證:所有的遵循該協議的子類,同樣能為構造器規定提供一個顯式的實現或繼承實現。
- 如果類已經被標記為final,那么不需要在協議構造器的實現中使用required修飾符。因為final類不能有子類。
- 如果一個子類重寫了父類的指定構造器,并且該構造器遵循了某個協議的規定,那么該構造器的實現需要被同時標示required和override修飾符
-
盡管協議本身并不實現任何功能,但是協議可以被當做類型來使用。
類專屬協議:你可以在協議的繼承列表中,通過添加class關鍵字,限制協議只能適配到類(class)類型。該class關鍵字必須是第一個出現在協議的繼承列表中,其后,才是其他繼承協議。
協議合成:有時候需要同時遵循多個協議。你可以將多個協議采用protocol<SomeProtocol, AnotherProtocol>這樣的格式進行組合,稱為協議合成(protocol composition)。你可以在<>中羅列任意多個你想要遵循的協議,以逗號分隔。
你可以使用is和as操作符來檢查是否遵循某一協議或強制轉化為某一類型。
在擴展協議的時候,可以指定一些限制,只有滿足這些限制的協議遵循者,才能獲得協議擴展提供的屬性和方法。這些限制寫在協議名之后,使用where關鍵字來描述限制情況。
注意:如果有多個協議擴展,而一個協議的遵循者又同時滿足它們的限制,那么將會使用所滿足限制最多的那個擴展。
-
Swift2.0筆記
最后編輯于 :
?著作權歸作者所有,轉載或內容合作請聯系作者
- 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
- 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
- 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
推薦閱讀更多精彩內容
- 本章將會介紹 存儲屬性的初始賦值自定義構造過程默認構造器值類型的構造器代理類的繼承和構造過程可失敗構造器必要構造器...