Swift基礎(chǔ)-17(泛型)

泛型代碼讓你能夠根據(jù)自定義的需求,編寫出適用于任意類型、靈活可重用的函數(shù)及類型瓣颅。它能讓你避免代碼的重復(fù),用一種清晰和抽象的方式來(lái)表達(dá)代碼的意圖譬正。

泛型是Swift最強(qiáng)大的特性之一宫补,許多 Swift 標(biāo)準(zhǔn)庫(kù)是通過(guò)泛型代碼構(gòu)建的。事實(shí)上曾我,泛型的使用貫穿了整本語(yǔ)言手冊(cè)粉怕,只是你可能沒(méi)有發(fā)現(xiàn)而已。例如抒巢,SwiftArrayDictionary 都是泛型集合贫贝。你可以創(chuàng)建一個(gè) Int數(shù)組,也可創(chuàng)建一個(gè)String數(shù)組,甚至可以是任意其他 Swift 類型的數(shù)組稚晚。同樣的崇堵,你也可以創(chuàng)建存儲(chǔ)任意指定類型的字典。

1.泛型所解決的問(wèn)題

下面是一個(gè)標(biāo)準(zhǔn)的非泛型函數(shù) swapTwoInts(::)客燕,用來(lái)交換兩個(gè) Int 值:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

這個(gè)函數(shù)使用輸入輸出參數(shù)(inout)來(lái)交換 a 和 b 的值鸳劳,請(qǐng)參考輸入輸出參數(shù)。

swapTwoInts(_:_:)函數(shù)交換 b 的原始值到 a也搓,并交換 a 的原始值到 b棍辕。你可以調(diào)用這個(gè)函數(shù)交換兩個(gè) Int 變量的值:

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// 打印 “someInt is now 107, and anotherInt is now 3”

誠(chéng)然,swapTwoInts(::) 函數(shù)挺有用还绘,但是它只能交換 Int 值楚昭,如果你想要交換兩個(gè) String 值或者Double`值,就不得不寫更多的函數(shù)拍顷。

2.泛型函數(shù)

泛型函數(shù)可以適用于任何類型抚太,下面的swapTwoValues(_:_:) 函數(shù)是上面三個(gè)函數(shù)的泛型版本:

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

swapTwoValues(_:_:)的函數(shù)主體和 swapTwoInts(_:_:)函數(shù)是一樣的,它們只在第一行有點(diǎn)不同昔案,如下所示:

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

占位類型名沒(méi)有指明 T 必須是什么類型尿贫,但是它指明了 a 和 b 必須是同一類型 T,無(wú)論 T 代表什么類型踏揣。

3.泛型類型

除了泛型函數(shù)庆亡,Swift 還允許你定義泛型類型。這些自定義類捞稿、結(jié)構(gòu)體和枚舉可以適用于任何類型又谋,類似于 ArrayDictionary

這部分內(nèi)容將向你展示如何編寫一個(gè)名為 Stack (棧)的泛型集合類型娱局。棧是一系列值的有序集合彰亥,和 Array類似,但它相比 SwiftArray 類型有更多的操作限制衰齐。數(shù)組允許在數(shù)組的任意位置插入新元素或是刪除其中任意位置的元素任斋。而棧只允許在集合的末端添加新的元素(稱之為入棧)。類似的耻涛,棧也只能從末端移除元素(稱之為出棧)废酷。

struct Stack<Element> {
    var items = [Element]()

    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

Element為待提供的類型定義了一個(gè)占位名。這種待提供的類型可以在結(jié)構(gòu)體的定義中通過(guò) Element 來(lái)引用抹缕。在這個(gè)例子中澈蟆,Element在如下三個(gè)地方被用作占位符:

創(chuàng)建 items 屬性,使用Element 類型的空數(shù)組對(duì)其進(jìn)行初始化歉嗓。
指定 push(_:)方法的唯一參數(shù) item的類型必須是 Element類型丰介。
指定 pop()方法的返回值類型必須是Element類型。

4.擴(kuò)展一個(gè)泛型類型

當(dāng)你擴(kuò)展一個(gè)泛型類型的時(shí)候,你并不需要在擴(kuò)展的定義中提供類型參數(shù)列表哮幢。原始類型定義中聲明的類型參數(shù)列表在擴(kuò)展中可以直接使用带膀,并且這些來(lái)自原始類型中的參數(shù)名稱會(huì)被用作原始定義中類型參數(shù)的引用。

下面的例子擴(kuò)展了泛型類型 Stack橙垢,為其添加了一個(gè)名為 topItem 的只讀計(jì)算型屬性垛叨,它將會(huì)返回當(dāng)前棧頂端的元素而不會(huì)將其從棧中移除:

extension Stack {
    var topItem: Element? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}

topItem 屬性會(huì)返回一個(gè) Element 類型的可選值。當(dāng)棧為空的時(shí)候柜某,topItem 會(huì)返回 nil嗽元;當(dāng)棧不為空的時(shí)候,topItem 會(huì)返回 items 數(shù)組中的最后一個(gè)元素喂击。

注意剂癌,這個(gè)擴(kuò)展并沒(méi)有定義一個(gè)類型參數(shù)列表。相反的翰绊,Stack 類型已有的類型參數(shù)名稱 Element佩谷,被用在擴(kuò)展中來(lái)表示計(jì)算型屬性 topItem 的可選類型。

計(jì)算型屬性 topItem 現(xiàn)在可以用來(lái)訪問(wèn)任意 Stack 實(shí)例的頂端元素且不移除它:

if let topItem = stackOfStrings.topItem {
    print("The top item on the stack is \(topItem).")
}
// 打印 “The top item on the stack is tres.”
5.類型約束語(yǔ)法

你可以在一個(gè)類型參數(shù)名后面放置一個(gè)類名或者協(xié)議名监嗜,并用冒號(hào)進(jìn)行分隔谐檀,來(lái)定義類型約束,它們將成為類型參數(shù)列表的一部分裁奇。對(duì)泛型函數(shù)添加類型約束的基本語(yǔ)法如下所示(作用于泛型類型時(shí)的語(yǔ)法與之相同):

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

上面這個(gè)函數(shù)有兩個(gè)類型參數(shù)桐猬。第一個(gè)類型參數(shù) T,有一個(gè)要求 T 必須是 SomeClass 子類的類型約束刽肠;第二個(gè)類型參數(shù) U溃肪,有一個(gè)要求 U 必須符合 SomeProtocol 協(xié)議的類型約束。

類型約束實(shí)踐

這里有個(gè)名為findIndex(ofString:in:)的非泛型函數(shù)五垮,該函數(shù)的功能是在一個(gè)String 數(shù)組中查找給定 String值的索引乍惊。若查找到匹配的字符串杜秸,findIndex(ofString:in:) 函數(shù)返回該字符串在數(shù)組中的索引值放仗,否則返回 nil:

func findIndex(ofString valueToFind: String, in array: [String]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

findIndex(ofString:in:) 函數(shù)可以用于查找字符串?dāng)?shù)組中的某個(gè)字符串:

let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
if let foundIndex = findIndex(ofString: "llama", in: strings) {
    print("The index of llama is \(foundIndex)")
}

如果上面的代碼改成泛型版本的寫法,

  func findIndex<T>(of valueToFind: T, in array:[T]) -> Int? {
        for (index, value) in array.enumerated() {
            if value == valueToFind {
                return index
            }
        }
        return nil
    }

這種寫法將編譯不通過(guò)撬碟,問(wèn)題出在相等性檢查上诞挨,即"if value == valueToFind"。不是所有的 Swift 類型都可以用等式符(==)進(jìn)行比較呢蛤。
Swift 標(biāo)準(zhǔn)庫(kù)中定義了一個(gè) Equatable 協(xié)議惶傻,該協(xié)議要求任何遵循該協(xié)議的類型必須實(shí)現(xiàn)等式符(==)及不等符(!=),從而能對(duì)該類型的任意兩個(gè)值進(jìn)行比較其障。所有的 Swift 標(biāo)準(zhǔn)類型自動(dòng)支持 Equatable 協(xié)議银室。
所以講上面的代碼改成下面的就可以了:

   func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
        for (index, value) in array.enumerated() {
            if value == valueToFind {
                return index
            }
        }
        return nil
    }
5.泛型where語(yǔ)句

類型約束讓你能夠?yàn)榉盒秃瘮?shù)或泛型類型的類型參數(shù)定義一些強(qiáng)制要求。

為關(guān)聯(lián)類型定義約束也是非常有用的。你可以在參數(shù)列表中通過(guò) where 子句為關(guān)聯(lián)類型定義約束蜈敢。你能通過(guò) where子句要求一個(gè)關(guān)聯(lián)類型遵從某個(gè)特定的協(xié)議辜荠,以及某個(gè)特定的類型參數(shù)和關(guān)聯(lián)類型必須類型相同。你可以通過(guò)將 where 關(guān)鍵字緊跟在類型參數(shù)列表后面來(lái)定義where子句抓狭,where 子句后跟一個(gè)或者多個(gè)針對(duì)關(guān)聯(lián)類型的約束伯病,以及一個(gè)或多個(gè)類型參數(shù)和關(guān)聯(lián)類型間的相等關(guān)系。你可以在函數(shù)體或者類型的大括號(hào)之前添加 where子句否过。

func allItemsMatch<C1: Container, C2: Container>(_ someContainer: C1, _ anotherContainer: C2) 
-> Bool where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {       
        // 檢查兩個(gè)容器含有相同數(shù)量的元素
        if someContainer.count != anotherContainer.count {
            return false
        }
 
        // 檢查每一對(duì)元素是否相等
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
 
        // 所有元素都匹配午笛,返回 true
        return true
}

演示使用:

var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
 
var arrayOfStrings = ["uno", "dos", "tres"]
 
if allItemsMatch(stackOfStrings, arrayOfStrings) {
    print("All items match.")
} else {
    print("Not all items match.")
}
// 打印 “All items match.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市苗桂,隨后出現(xiàn)的幾起案子药磺,更是在濱河造成了極大的恐慌,老刑警劉巖煤伟,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件与涡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡持偏,警方通過(guò)查閱死者的電腦和手機(jī)驼卖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鸿秆,“玉大人酌畜,你說(shuō)我怎么就攤上這事∏溥矗” “怎么了景鼠?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵背稼,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)所刀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任泣崩,我火速辦了婚禮昔头,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘考杉。我一直安慰自己策精,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布崇棠。 她就那樣靜靜地躺著咽袜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪枕稀。 梳的紋絲不亂的頭發(fā)上询刹,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天谜嫉,我揣著相機(jī)與錄音,去河邊找鬼凹联。 笑死骄恶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的匕垫。 我是一名探鬼主播僧鲁,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼象泵!你這毒婦竟也來(lái)了寞秃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤偶惠,失蹤者是張志新(化名)和其女友劉穎春寿,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忽孽,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绑改,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了兄一。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片厘线。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖出革,靈堂內(nèi)的尸體忽然破棺而出造壮,到底是詐尸還是另有隱情,我是刑警寧澤骂束,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布耳璧,位于F島的核電站,受9級(jí)特大地震影響展箱,放射性物質(zhì)發(fā)生泄漏旨枯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一混驰、第九天 我趴在偏房一處隱蔽的房頂上張望攀隔。 院中可真熱鬧,春花似錦账胧、人聲如沸竞慢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至遮精,卻和暖如春居夹,著一層夾襖步出監(jiān)牢的瞬間败潦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工准脂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留劫扒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓狸膏,卻偏偏與公主長(zhǎng)得像沟饥,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子湾戳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 本章將會(huì)介紹 泛型所解決的問(wèn)題泛型函數(shù)類型參數(shù)命名類型參數(shù)泛型類型擴(kuò)展一個(gè)泛型類型類型約束關(guān)聯(lián)類型泛型 Where...
    寒橋閱讀 639評(píng)論 0 2
  • 泛型(Generics) 泛型代碼允許你定義適用于任何類型的,符合你設(shè)置的要求的,靈活且可重用的 函數(shù)和類型贤旷。泛型...
    果啤閱讀 678評(píng)論 0 0
  • 136.泛型 泛型代碼讓你可以寫出靈活,可重用的函數(shù)和類型,它們可以使用任何類型,受你定義的需求的約束。你可以寫出...
    無(wú)灃閱讀 1,472評(píng)論 0 4
  • 泛型代碼可以確保你寫出靈活的砾脑,可重用的函數(shù)和定義出任何你所確定好的需求的類型幼驶。你的可以寫出避免重復(fù)的代碼,并且用一...
    iOS_Developer閱讀 800評(píng)論 0 0
  • 這輩子韧衣,與那么多人遇見(jiàn)過(guò)盅藻,與那么多人交談過(guò),然而聽(tīng)過(guò)最多的畅铭,記得最多的還是家里面那位伶牙俐齒的小老太太說(shuō)過(guò)的話氏淑。 ...
    強(qiáng)說(shuō)愁閱讀 231評(píng)論 0 0