Swift-泛型筆記

Swift 泛型

Swift 提供了泛型讓你寫出靈活且可重用的函數(shù)和類型座咆。

Swift 標準庫是通過泛型代碼構建出來的介陶。

Swift 的數(shù)組和字典類型都是泛型集哺呜。

你可以創(chuàng)建一個Int數(shù)組某残,也可創(chuàng)建一個String數(shù)組,或者甚至于可以是任何其他 Swift 的類型數(shù)據數(shù)組玻墅。

以下實例是一個非泛型函數(shù) exchange 用來交換兩個 Int 值:

實例

// 定義一個交換兩個變量的函數(shù)
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}
 
var numb1 = 100
var numb2 = 200
 
print("交換前數(shù)據: \(numb1) 和 \(numb2)")
swapTwoInts(&numb1, &numb2)
print("交換后數(shù)據: \(numb1) 和 \(numb2)")

以上程序執(zhí)行輸出結果為:

交換前數(shù)據: 100 和 200
交換后數(shù)據: 200 和 100

以上實例只試用與交換整數(shù) Int 類型的變量澳厢。如果你想要交換兩個 String 值或者 Double 值剩拢,就得重新寫個對應的函數(shù)裸扶,例如 swapTwoStrings(::) 和 swapTwoDoubles(::),如下所示:

String 和 Double 值交換函數(shù)

func swapTwoStrings(_ a: inout String, _ b: inout String) {
    let temporaryA = a
    a = b
    b = temporaryA
}
 
func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
    let temporaryA = a
    a = b
    b = temporaryA
}

從以上代碼來看,它們功能代碼是相同的摸屠,只是類型上不一樣粱哼,這時我們可以使用泛型揭措,從而避免重復編寫代碼绊含。

泛型使用了占位類型名(在這里用字母 T 來表示)來代替實際類型名(例如 Int、String 或 Double)逃顶。

func swapTwoValues<T>(_ a: inout T, _ b: inout T)

swapTwoValues 后面跟著占位類型名(T)以政,并用尖括號括起來(<T>)盈蛮。這個尖括號告訴 Swift 那個 T 是 swapTwoValues(::) 函數(shù)定義內的一個占位類型名技矮,因此 Swift 不會去查找名為 T 的實際類型穆役。

以下實例是一個泛型函數(shù) exchange 用來交換兩個 Int 和 String 值:

實例

// 定義一個交換兩個變量的函數(shù)
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}
 
var numb1 = 100
var numb2 = 200
 
print("交換前數(shù)據:  \(numb1) 和 \(numb2)")
swapTwoValues(&numb1, &numb2)
print("交換后數(shù)據: \(numb1) 和 \(numb2)")
 
var str1 = "A"
var str2 = "B"
 
print("交換前數(shù)據:  \(str1) 和 \(str2)")
swapTwoValues(&str1, &str2)
print("交換后數(shù)據: \(str1) 和 \(str2)")

以上程序執(zhí)行輸出結果為:

交換前數(shù)據:  100 和 200
交換后數(shù)據: 200 和 100
交換前數(shù)據:  A 和 B
交換后數(shù)據: B 和 A

泛型類型

Swift 允許你定義你自己的泛型類型耿币。

自定義類、結構體和枚舉作用于任何類型叛溢,如同 Array 和 Dictionary 的用法楷掉。

接下來我們來編寫一個名為 Stack (棧)的泛型集合類型烹植,棧只允許在集合的末端添加新的元素(稱之為入棧),且也只能從末端移除元素(稱之為出棧)草雕。

image

圖片中從左到右解析如下:

  • 三個值在棧中。
  • 第四個值被壓入到棧的頂部憨琳。
  • 現(xiàn)在有四個值在棧中,最近入棧的那個值在頂部菌湃。
  • 棧中最頂部的那個值被移除慢味,或稱之為出棧墅冷。
  • 移除掉一個值后,現(xiàn)在棧又只有三個值了驰唬。

以下實例是一個非泛型版本的棧腔彰,以 Int 型的棧為例:

Int 型的棧

struct IntStack {
    var items = [Int]()
    mutating func push(_ item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
}

這個結構體在棧中使用一個名為 items 的 Array 屬性來存儲值搓逾。Stack 提供了兩個方法:push(_:) 和 pop()霞篡,用來向棧中壓入值以及從棧中移除值。這些方法被標記為 mutating朗兵,因為它們需要修改結構體的 items 數(shù)組余掖。

上面的 IntStack 結構體只能用于 Int 類型盐欺。不過,可以定義一個泛型 Stack 結構體歌憨,從而能夠處理任意類型的值。

下面是相同代碼的泛型版本:

泛型的棧

struct Stack<Element> {
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}
 
var stackOfStrings = Stack<String>()
print("字符串元素入棧: ")
stackOfStrings.push("google")
stackOfStrings.push("runoob")
print(stackOfStrings.items);
 
let deletetos = stackOfStrings.pop()
print("出棧元素: " + deletetos)
 
var stackOfInts = Stack<Int>()
print("整數(shù)元素入棧: ")
stackOfInts.push(1)
stackOfInts.push(2)
print(stackOfInts.items);

實例執(zhí)行結果為:

字符串元素入棧: 
["google", "runoob"]
出棧元素: runoob
整數(shù)元素入棧: 
[1, 2]

Stack 基本上和 IntStack 相同漆改,占位類型參數(shù) Element 代替了實際的 Int 類型挫剑。

以上實例中 Element 在如下三個地方被用作占位符:

  • 創(chuàng)建 items 屬性樊破,使用 Element 類型的空數(shù)組對其進行初始化哲戚。
  • 指定 push(_:) 方法的唯一參數(shù) item 的類型必須是 Element 類型顺少。
  • 指定 pop() 方法的返回值類型必須是 Element 類型脆炎。

擴展泛型類型

當你擴展一個泛型類型的時候(使用 extension 關鍵字)秒裕,你并不需要在擴展的定義中提供類型參數(shù)列表几蜻。更加方便的是喇潘,原始類型定義中聲明的類型參數(shù)列表在擴展里是可以使用的响蓉,并且這些來自原始類型中的參數(shù)名稱會被用作原始定義中類型參數(shù)的引用哨毁。

下面的例子擴展了泛型類型 Stack,為其添加了一個名為 topItem 的只讀計算型屬性想幻,它將會返回當前棧頂端的元素而不會將其從棧中移除:

泛型

struct Stack<Element> {
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}
 
extension Stack {
    var topItem: Element? {
       return items.isEmpty ? nil : items[items.count - 1]
    }
}
 
var stackOfStrings = Stack<String>()
print("字符串元素入棧: ")
stackOfStrings.push("google")
stackOfStrings.push("runoob")
 
if let topItem = stackOfStrings.topItem {
    print("棧中的頂部元素是:\(topItem).")
}
 
print(stackOfStrings.items)

實例中 topItem 屬性會返回一個 Element 類型的可選值脏毯。當棧為空的時候食店,topItem 會返回 nil吉嫩;當棧不為空的時候嗅定,topItem 會返回 items 數(shù)組中的最后一個元素自娩。

以上程序執(zhí)行輸出結果為:

字符串元素入棧: 
棧中的頂部元素是:runoob.
["google", "runoob"]

我們也可以通過擴展一個存在的類型來指定關聯(lián)類型。

例如 Swift 的 Array 類型已經提供 append(_:) 方法渠退,一個 count 屬性忙迁,以及一個接受 Int 類型索引值的下標用以檢索其元素。這三個功能都符合 Container 協(xié)議的要求碎乃,所以你只需簡單地聲明 Array 采納該協(xié)議就可以擴展 Array姊扔。

以下實例創(chuàng)建一個空擴展即可:

extension Array: Container {}

類型約束

類型約束指定了一個必須繼承自指定類的類型參數(shù),或者遵循一個特定的協(xié)議或協(xié)議構成荠锭。

類型約束語法

你可以寫一個在一個類型參數(shù)名后面的類型約束旱眯,通過冒號分割,來作為類型參數(shù)鏈的一部分证九。這種作用于泛型函數(shù)的類型約束的基礎語法如下所示(和泛型類型的語法相同):

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 這里是泛型函數(shù)的函數(shù)體部分
}

上面這個函數(shù)有兩個類型參數(shù)。第一個類型參數(shù) T呀页,有一個要求 T 必須是 SomeClass 子類的類型約束;第二個類型參數(shù) U,有一個要求 U 必須符合 SomeProtocol 協(xié)議的類型約束定续。

實例

泛型

// 非泛型函數(shù)恩掷,查找指定字符串在數(shù)組中的索引
func findIndex(ofString valueToFind: String, in array: [String]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            // 找到返回索引值
            return index
        }
    }
    return nil
}
 
 
let strings = ["google", "weibo", "taobao", "runoob", "facebook"]
if let foundIndex = findIndex(ofString: "runoob", in: strings) {
    print("runoob 的索引為 \(foundIndex)")
}

索引下標從 0 開始。

以上程序執(zhí)行輸出結果為:

runoob 的索引為 3

關聯(lián)類

Swift 中使用 associatedtype 關鍵字來設置關聯(lián)類型實例。

下面例子定義了一個 Container 協(xié)議望忆,該協(xié)議定義了一個關聯(lián)類型 ItemType歉备。

Container 協(xié)議只指定了三個任何遵從 Container 協(xié)議的類型必須提供的功能。遵從協(xié)議的類型在滿足這三個條件的情況下也可以提供其他額外的功能书闸。

// Container 協(xié)議
protocol Container {
    associatedtype ItemType
    // 添加一個新元素到容器里
    mutating func append(_ item: ItemType)
    // 獲取容器中元素的數(shù)
    var count: Int { get }
    // 通過索引值類型為 Int 的下標檢索到容器中的每一個元素
    subscript(i: Int) -> ItemType { get }
}

// Stack 結構體遵從 Container 協(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]
    }
}

var tos = Stack<String>()
tos.push("google")
tos.push("runoob")
tos.push("taobao")
// 元素列表
print(tos.items)
// 元素個數(shù)
print( tos.count)

以上程序執(zhí)行輸出結果為:

["google", "runoob", "taobao"]
3

Where 語句

類型約束能夠確保類型符合泛型函數(shù)或類的定義約束。

你可以在參數(shù)列表中通過where語句定義參數(shù)的約束磷籍。

你可以寫一個where語句晒旅,緊跟在在類型參數(shù)列表后面,where語句后跟一個或者多個針對關聯(lián)類型的約束拟烫,以及(或)一個或多個類型和關聯(lián)類型間的等價(equality)關系置媳。

實例

下面的例子定義了一個名為allItemsMatch的泛型函數(shù)靶橱,用來檢查兩個Container實例是否包含相同順序的相同元素。

如果所有的元素能夠匹配,那么返回 true啤覆,反之則返回 false笨觅。

泛型

// Container 協(xié)議
protocol Container {
    associatedtype ItemType
    // 添加一個新元素到容器里
    mutating func append(_ item: ItemType)
    // 獲取容器中元素的數(shù)
    var count: Int { get }
    // 通過索引值類型為 Int 的下標檢索到容器中的每一個元素
    subscript(i: Int) -> ItemType { get }
}
 
// // 遵循Container協(xié)議的泛型TOS類型
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]
    }
}
// 擴展,將 Array 當作 Container 來使用
extension Array: Container {}
 
func allItemsMatch<C1: Container, C2: Container>
    (_ someContainer: C1, _ anotherContainer: C2) -> Bool
    where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
        
        // 檢查兩個容器含有相同數(shù)量的元素
        if someContainer.count != anotherContainer.count {
            return false
        }
        
        // 檢查每一對元素是否相等
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
        
        // 所有元素都匹配疗琉,返回 true
        return true
}
var tos = Stack<String>()
tos.push("google")
tos.push("runoob")
tos.push("taobao")
 
var aos = ["google", "runoob", "taobao"]
 
if allItemsMatch(tos, aos) {
    print("匹配所有元素")
} else {
    print("元素不匹配")
}

以上程序執(zhí)行輸出結果為:

匹配所有元素
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子坚俗,更是在濱河造成了極大的恐慌降允,老刑警劉巖尉剩,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烛占,死亡現(xiàn)場離奇詭異浦辨,居然都是意外死亡旦装,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來烟瞧,“玉大人青灼,你說我怎么就攤上這事±鲆眩” “怎么了够挂?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長赋铝。 經常有香客問我,道長良哲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蔫浆,結果婚禮上挠唆,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好豌鹤,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著趴荸,像睡著了一般儒溉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上发钝,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天顿涣,我揣著相機與錄音,去河邊找鬼酝豪。 笑死涛碑,一個胖子當著我的面吹牛,可吹牛的內容都是我干的孵淘。 我是一名探鬼主播蒲障,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瘫证!你這毒婦竟也來了揉阎?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤背捌,失蹤者是張志新(化名)和其女友劉穎毙籽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毡庆,經...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡坑赡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年巡扇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垮衷。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖乖坠,靈堂內的尸體忽然破棺而出搀突,到底是詐尸還是另有隱情,我是刑警寧澤熊泵,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布仰迁,位于F島的核電站,受9級特大地震影響顽分,放射性物質發(fā)生泄漏徐许。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一卒蘸、第九天 我趴在偏房一處隱蔽的房頂上張望雌隅。 院中可真熱鬧,春花似錦缸沃、人聲如沸恰起。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽检盼。三九已至,卻和暖如春翘单,著一層夾襖步出監(jiān)牢的瞬間吨枉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工哄芜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留貌亭,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓忠烛,卻偏偏與公主長得像属提,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子美尸,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內容

  • 本章將會介紹 泛型所解決的問題泛型函數(shù)類型參數(shù)命名類型參數(shù)泛型類型擴展一個泛型類型類型約束關聯(lián)類型泛型 Where...
    寒橋閱讀 631評論 0 2
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 3,776評論 1 10
  • 泛型代碼可以確保你寫出靈活的冤议,可重用的函數(shù)和定義出任何你所確定好的需求的類型。你的可以寫出避免重復的代碼师坎,并且用一...
    iOS_Developer閱讀 790評論 0 0
  • 在我死后 一把火燒了我 請聽聽我臨死時的拜托 燒我的時候 先搜集來一堆枯葉 鋪兩層 然后將我平趴在上面 這樣你們就...
    油茶花先生閱讀 346評論 0 0
  • 時光匆匆恕酸,為期一個半月的思維導圖課結束了。 課程里胯陋,老師和孩子們不但是師生關系蕊温,也是朋友關系袱箱。 六次的課程,作業(yè)义矛,...
    新華書單閱讀 527評論 0 0