版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2017.08.01 |
前言
我是swift2.0的時候開始接觸的,記得那時候還不是很穩定,公司的項目也都是用oc做的,并不對swift很重視,我自己學了一段時間,到現在swift3.0+已經出來了,自己平時也不寫,忘記的也差不多了,正好項目這段時間已經上線了,不是很忙,我就可以每天總結一點了,希望對自己對大家有所幫助。在總結的時候我會對比oc進行說明,有代碼的我會給出相關比對代碼。
1. swift簡單總結(一)—— 數據簡單值和類型轉換
2. swift簡單總結(二)—— 簡單值和控制流
3. swift簡單總結(三)—— 循環控制和函數
4. swift簡單總結(四)—— 函數和類
5. swift簡單總結(五)—— 枚舉和結構體
6. swift簡單總結(六)—— 協議擴展與泛型
7. swift簡單總結(七)—— 數據類型
8. swift簡單總結(八)—— 別名、布爾值與元組
9. swift簡單總結(九)—— 可選值和斷言
10. swift簡單總結(十)—— 運算符
11. swift簡單總結(十一)—— 字符串和字符
12. swift簡單總結(十二)—— 集合類型之數組
13. swift簡單總結(十三)—— 集合類型之字典
14. swift簡單總結(十四)—— 控制流
15. swift簡單總結(十五)—— 控制轉移語句
16. swift簡單總結(十六)—— 函數
17. swift簡單總結(十七)—— 閉包(Closures)
18. swift簡單總結(十八)—— 枚舉
19. swift簡單總結(十九)—— 類和結構體
20. swift簡單總結(二十)—— 屬性
21. swift簡單總結(二十一)—— 方法
22. swift簡單總結(二十二)—— 下標腳本
23. swift簡單總結(二十三)—— 繼承
24. swift簡單總結(二十四)—— 構造過程
25. swift簡單總結(二十五)—— 構造過程
26. swift簡單總結(二十六)—— 析構過程
27. swift簡單總結(二十七)—— 自動引用計數
28. swift簡單總結(二十八)—— 可選鏈
29. swift簡單總結(二十九)—— 類型轉換
30.swift簡單總結(三十)—— 嵌套類型
31.swift簡單總結(三十一)—— 擴展
32.swift簡單總結(三十二)—— 協議
泛型
泛型是swift
強大特性中的其中一個,例如:swift
數組和字典類型都是泛型集,你可以創建一個Int
數組,也可創建一個String
數組或者甚至于可以是任何其他swift
的類型數據數組。
下面主要從幾點進行講述:
- 泛型所解決的問題
- 泛型函數
- 類型參數
- 命名類型參數
- 泛型類型
- 類型約束
- 關聯類型
-
where
語句
泛型所解決的問題
先看一個例子,標準的非泛型函數,用來交換交換Int
的值。
class JJPracticeVC: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
var someInt = 3
var anotherInt = 107
swapTwoInts(a: &someInt, b: &anotherInt)
print("someInt : \(someInt), anotherInt : \(anotherInt)")
}
func swapTwoInts(a : inout Int, b : inout Int){
let temp = a
a = b
b = temp
}
}
下面看輸出結果
someInt : 107, anotherInt : 3
swapTwoInts
函數是非常有用的,但是它只能交換Int
的值,如果你想要交換兩個Double
和String
值,就不得不寫更多的函數了,例如swapTwoDoubles
和swapTwoString
。
但是,在實際應用中,通常需要一個用處更強大并且盡可能的考慮更多的靈活性的單個函數,可以用來交換兩個任意類型值,這里泛型就能幫你解決這個問題。
注意:swift
中不同類型的值不能互換,所以a
、b
和temp
必須是同類型的,否則交換會報錯。
泛型函數
泛型函數可以工作與任何類型,這里是一個swapTwoInts
函數的泛型版本,用于交換兩個值。
class JJPracticeVC: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
var someInt = 3
var anotherInt = 107
swapTwoValue(a: &someInt, b: &anotherInt)
print("someInt : \(someInt), anotherInt : \(anotherInt)")
var someString = " hello "
var anotherString = " world "
swapTwoValue(a: &someString, b: &anotherString)
print("someString : \(someString), anotherString : \(anotherString)")
}
func swapTwoValue <T>(a : inout T, b : inout T){
let temp = a
a = b
b = temp
}
}
下面看輸出結果
someInt : 107, anotherInt : 3
someString : world , anotherString : hello
上面利用泛型,可以交換任意兩個相同類型的值。這里T
是一個占位命名類型,swift
不會查找命名為T
的實際類型。
類型參數
在上面的例子swapTwoValue
中,占位類型T
是一種類型參數的示例,類型參數指定并命名為一個占位類型,并且緊隨在函數名后面,使用一對尖括號括起來<T>
。
你可支持多個類型參數,命名在尖括號中,用逗號分開。
命名類型參數
在簡答的情況下,泛型函數或泛型類型需要指定一個占位類型,通常用一個單個字母T
命名類型參數,不過,你可以使用任何有效的標識符來作為類型參數名。
如果你使用多個參數定義更復雜的泛型函數或泛型類型,那么使用更多的描述類型參數是非常有用的,例如,swift
字典dictionary類型就有兩個類型參數,一個是鍵,另外一個就是值。
請始終使用大寫字母開頭的駝峰命名法,例如T
和KeyType
來給類型參數命名,以表明它們是類型的占位符,而非類型值。
泛型類型
在泛型函數中,swift
允許你定義自己的泛型類型,這些自定義類、結構體和枚舉作用與任何類型。
下面展示的是泛型集類型,stack
棧。
有棧就有入棧和出棧等操作,如上圖展示。
struct IntStruct {
var items = [Int]()
mutating func push(item : Int){
items.append(item)
}
mutating func pop() ->Int{
return items.removeLast()
}
}
上面的結構體就展示的是入棧和出棧等操作。上面的只限于整型值,下面我們對其進行擴展,以適應各種類型。
struct Stack <T> {
var items = [T]()
mutating func push(item : T){
items.append(item)
}
mutating func pop() ->T{
return items.removeLast()
}
}
下面調用一下
class JJPracticeVC: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
var stackOfString = Stack<String>()
stackOfString.push(item: "uno")
stackOfString.push(item: "dos")
stackOfString.push(item: "tres")
stackOfString.push(item: "cuatro")
print(stackOfString)
}
}
下面看輸出結果
Stack<String>(items: ["uno", "dos", "tres", "cuatro"])
下面看一下入棧示意圖。
下面我們在看一下出棧
stackOfString.pop()
看一下輸出結果
Stack<String>(items: ["uno", "dos", "tres"])
下面看一下出棧示意圖。
類型約束
雖然前面的都是定義任何類型的泛型,但是有時候強制限制類型是很有用的,類型約束指定了一個必須繼承自指定類的類型參數,或者遵循一個特定的協議或協議構成。比如:swift
字典的鍵類型就進行了約束,一定要是可哈希的,鍵必須遵循哈希協議Hashable
,所有swift
基本類型如Int
、String
、Double
和Bool
都是可哈希的。
1. 類型約束語法
你可以寫一個在一個類型參數名后面的類型約束,通過冒號分割,來作為類型參數鏈的一部分,這種作用于泛型函數的類型約束的基礎語法如下所示。
func someFunction<T : SomeClass, U : SomeProtocol> (someT : T, someU : U) {
//function body goes here
}
上面語法實例匯總,有類型參數,一個是T
,有一個需要T
必須是SomeClass
子類的類型約束;第二個類型參數U
,有一個需要U
遵循SomeProtcol
協議的類型約束。
2. 類型約束行為
先看一個簡單例子。
class JJPracticeVC: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
let stringArr = ["cat", "dog", "llama", "parakeet", "terrapin"]
if let foundIndex = findStringIndex(array: stringArr, valueToFind: "llama") {
print("The index of llama is \(foundIndex)")
}
}
func findStringIndex(array : [String], valueToFind : String) -> Int?{
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
}
下面是輸出結果
The index of llama is 2
上面這個實例的功能就是查找數組中指定元素額的下標,這里只是針對字符串而言有效,其實我們還可以擴展到指定類型有效。
func findIndex<T>(array : [T], valueToFind : T) -> Int?{
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
上面就是擴展后的實例,但是這個不會編譯通過,報錯。問題出在if value == valueToFind
上面,不是所有的swift
中的類型都可以用等式符==
進行比較,比如,你自己創建一個你自己的類或結構體來表示一個復雜的數據模型,但是swift
沒法猜到對于這個類或結構體而言“等于”的意思,正因如此,這部分代碼不能保證工作于每個可能的類型T
。
不過,這個可以解決,swift
標準庫中定義了一個Equatable
協議,該協議要求任何遵循的類型實現等式符==
和不等式符!=
對任何兩個該類型進行比較,所有的swift
標準類型自動支持Equatable
協議。
下面我們改進一下代碼。
class JJPracticeVC: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
let stringArr = ["cat", "dog", "llama", "parakeet", "terrapin"]
let stringResult = findIndex(array: stringArr, valueToFind: "dog")
print("stringResult \(stringResult)")
let doubleArr = [3.14, 4.34, 1.00, 252.9]
let doubleResult = findIndex(array: doubleArr, valueToFind: 3.14)
print("doubleResult \(doubleResult)")
}
func findIndex<T : Equatable>(array : [T], valueToFind : T) -> Int?{
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
}
下面看一下輸出結果
stringResult Optional(1)
doubleResult Optional(0)
這里,findIndex
中<T : Equatable>
意味著“任何T類型都遵循Equatable
協議”。
關聯類型 - associatedtype
當定義一個協議時,有時候需要聲明一個或多個關聯類型作為協議定義的一部分是非常有用的,一個關聯類型作為協議的一部分,給定了類型的一個占位名(或別名),作用于關聯類型上實際類型在協議被實現前是不需要指定的,關聯類型被指定為associatedtype
關鍵字。
1. 關聯類型行為
下面是一個簡單例子。
protocol Container {
associatedtype ItemType
mutating func append(item : ItemType)
var count : Int{
get
}
subscript(i : Int) -> ItemType{
get
}
}
Container
協議定義了三個任何容器必須支持的兼容要求
- 必須可能通過
append
方法添加一個新的item
到容器里; - 必須可能通過使用
count
屬性獲取items
的數量,并返回一個Int
值; - 必須可能通過容器的
Int
索引值下標可以檢索到每一個item
。
Container
協議沒有指定容器里的item
是如何存儲或何種類型是允許的,這個協議只指定三個任何遵循Container
類型所必須支持的功能點,一個遵循的類型在滿足這三個條件的情況下也可以提供其他額外的功能。
任何遵循Container
協議的類型必須指定存儲在其里面的值類型,必須保證只有正確類型的items
可以加進容器里面,必須明確可以通過其下標返回item
類型。
為此,定義了ItemType
的關聯類型,寫作associatedtype
,這個協議不會定義ItemType
是什么別名,這個信息將由任何遵循協議的類型來提供。
下面看一個簡單例子。
struct IntStack : Container{
var items = [Int]()
mutating func push(item : Int){
items.append(item)
}
mutating func pop() -> Int{
return items.removeLast()
}
//遵循Container協議的實現
typealias ItemType = Int
mutating func append(item: Int) {
self.push(item: item)
}
var count: Int{
return items.count
}
subscript(i : Int) -> Int{
return items[i]
}
}
上面就是將"Int"和ItemType
進行關聯,下面我們就看一下泛型在這里的使用。
struct Stack<T> : Container{
var items = [T]()
mutating func push(item : T){
items.append(item)
}
mutating func pop() -> T{
return items.removeLast()
}
//遵循Container協議的實現
mutating func append(item: T) {
self.push(item: item)
}
var count: Int{
return items.count
}
subscript(i : Int) -> T{
return items[i]
}
}
2. 擴展一個存在的類型為一指定關聯類型
前面定義了Container
協議,意味著你可以擴展Array
去遵循Container
協議。只需要簡單的聲明Array
適用于該協議而已。下面定義的就是實現一個空擴展行為。
extension Array : Container{
}
Where語句
對關聯類型定義約束是非常有有用的,你可以在參數列表中通過where
語句定義參數的約束,一個where
語句能夠使一個關聯類型遵循一個特定的協議,以及那個特定的類型參數和關聯類型可以是相同的,可以寫一個where
語句,緊跟在類型參數列表后面,where
語句后跟一個或者多個針對關聯類型的約束,以及一個或多個類型和關聯類型間的等價關系。
看下面這個簡單例子。
func allItemsMatch<C1 : Container, C2 : Container where C1.ItemType == C2.ItemType, C1.ItemType : Equatable>(someContainer : C1, anotherContainer : C2) -> Bool{
if someContainer.count != anotherContainer.count {
return false
}
for i in 0...someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
return true
}
這個函數的作用就是檢查兩個Container
實例是否包含相同順序的相同元素。這個函數的類型參數緊隨在兩個類型參數需求的后面。
-
C1
必須遵循Container
協議 -
C2
必須遵循Container
協議 -
C1
的ItemType
同樣也是C2
的ItemType
-
C1
的ItemType
必須遵守Equatable
協議。
后記
未完,待續~~~