Swift泛型介紹
泛型是為Swift編程靈活性的一種語法,在函數(shù)、枚舉、結(jié)構(gòu)體、類中都得到充分的應(yīng)用,它的引入可以起到占位符的作用,當(dāng)類型暫時(shí)不確定的,只有等到調(diào)用函數(shù)時(shí)才能確定具體類型的時(shí)候可以引入泛型。
我們之前實(shí)際上已經(jīng)使用過泛型,例如:Swift的Array和Dictionary類型都是泛型集。
你可以創(chuàng)建一個(gè)Int數(shù)組,也可創(chuàng)建一個(gè)String數(shù)組,或者甚至于可以是任何其他Swift的類型數(shù)據(jù)數(shù)組。同樣的,你也可以創(chuàng)建存儲任何指定類型的字典(Dictionary),而且這些類型可以是沒有限制的。
我們?yōu)槭裁匆褂梅盒湍??下面有個(gè)例子可以簡單說明使用泛型的好處
// 定義一個(gè)函數(shù),要求追加數(shù)組數(shù)據(jù)到指定一個(gè)數(shù)組中
func appendIntToArray(src:[Int],inout dest:[Int]) {
// 遍歷并加到數(shù)組后邊
for element in src {
dest.append(element)
}
}
// 使用appendIntToArray添加整形數(shù)組數(shù)據(jù)
var arr = [2,5]
appendIntToArray([12,9], dest: &arr)
print(arr) // [2,5,12,9]
// 那么再要求讓你實(shí)現(xiàn)添加字符串呢,好吧重寫一個(gè)
func appendStringToArray(src:[String],inout dest:[String]) {
for element in src {
dest.append(element)
}
}
var strArr = ["OC","Swift"]
appendStringToArray(["PHP", "C#"], dest: &strArr)
print(strArr)
// 如果有需要你實(shí)現(xiàn)添加其他類型呢?
// 是不是每個(gè)類型都需要寫一個(gè)對應(yīng)的函數(shù)去實(shí)現(xiàn),那這樣就太復(fù)雜了!這時(shí)候我們就需要使用泛型
// 定義泛型函數(shù),在普通函數(shù)名后面加上<T>,T是個(gè)類型占用符,可以表示任何類型
func appendArray<T>(src:[T],inout dest:[T]) {
for element in src {
dest.append(element)
}
}
// 看到如此強(qiáng)大了吧?然后隨意使用
var arr2 = [5,8]
appendArray([9,58], dest: &arr2) // appendArray自動識別要添加的數(shù)組數(shù)據(jù)類型
print(arr2) // [5, 8, 9, 58]
var strArr2 = ["renhairui","hello"]
appendArray(["nihao", "helloworld"], dest: &strArr2)
print(strArr2) // ["renhairui", "hello", "nihao", "helloworld"]
var doubleArr = [1.2,3.4]
appendArray([6.5,1.0], dest: &doubleArr)
print(doubleArr) // [1.2, 3.4, 6.5, 1.0]
我的理解:泛型就是先占坑,具體占坑做什么,隨你
Swift泛型使用
Swift泛型相關(guān)使用可分為以下幾點(diǎn):
泛型函數(shù)
泛型類型
泛型約束
泛型協(xié)議
泛型函數(shù),函數(shù)參數(shù)或返回值類型用泛型表示
// 泛型函數(shù)定義式
func 函數(shù)名<泛型1,泛型2,…>(形參列表)->返回值類型
{
// 函數(shù)體...
}
泛型函數(shù)使用實(shí)例
// 定義一個(gè)泛型函數(shù),把2個(gè)參數(shù)的值進(jìn)行交換
func swapTwoValues<T>(inout valueOne:T ,inout valueTwo:T) {
let temporaryA = valueOne
valueOne = valueTwo
valueTwo = temporaryA
}
var oneInt = 3
var twoInt = 107
swapTwoValues(&oneInt, valueTwo: &twoInt)
print("oneInt = \(oneInt), twoInt = \(twoInt)") // oneInt = 107, twoInt = 3
var oneStr = "Hello"
var twoStr = "World"
swapTwoValues(&oneStr, valueTwo: &twoStr)
print("oneStr = \(oneStr), twoStr = \(twoStr)") // oneStr = world, twoStr = hello
泛型類型,在定義類型時(shí)使用泛型
使用也和泛型函數(shù)差不多,就是在類型名后面加上<泛型1,泛型2,…>,然后在類型里面直接使用泛型即可
通常在泛型函數(shù)中,Swift 允許你定義你自己的泛型類型。這些自定義類、結(jié)構(gòu)體和枚舉作用于任何類型,如同Array和Dictionary的用法。
這部分向你展示如何寫一個(gè)泛型集類型--Stack(棧)。一個(gè)棧是一系列值域的集合,和Array(數(shù)組)類似,但其是一個(gè)比 Swift 的Array類型更多限制的集合。一個(gè)數(shù)組可以允許其里面任何位置的插入/刪除操作,而棧,只允許在集合的末端添加新的項(xiàng)(如同push一個(gè)新值進(jìn)棧)。同樣的一個(gè)棧也只能從末端移除項(xiàng)(如同pop一個(gè)值出棧)。
注意 棧的概念已被UINavigationController類使用來模擬視圖控制器的導(dǎo)航結(jié)構(gòu)。
你通過調(diào)用UINavigationController的pushViewController(:animated:)方法來為導(dǎo)航棧添加(add)新的視圖控制器;
而通過popViewControllerAnimated(:)的方法來從導(dǎo)航棧中移除(pop)某個(gè)視圖控制器。
每當(dāng)你需要一個(gè)嚴(yán)格的后進(jìn)先出方式來管理集合,堆棧都是最實(shí)用的模型。
下圖展示了一個(gè)棧的壓棧(push)/出棧(pop)的行為:
現(xiàn)在有三個(gè)值在棧中;
第四個(gè)值“pushed”到棧的頂部;
現(xiàn)在有四個(gè)值在棧中,最近的那個(gè)在頂部;
棧中最頂部的那個(gè)項(xiàng)被移除,或稱之為“popped”;
移除掉一個(gè)值后,現(xiàn)在棧又重新只有三個(gè)值。
這里展示了如何寫一個(gè)非泛型版本的棧,Int值型的棧:
// 這里展示了如何寫一個(gè)非泛型版本的棧,Int值型的棧:
struct IntStack {
var items = [Int]()
mutating func push(item:Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
}
這個(gè)結(jié)構(gòu)體在棧中使用一個(gè)Array性質(zhì)的items存儲值。Stack提供兩個(gè)方法:push和pop,從棧中壓進(jìn)一個(gè)值和移除一個(gè)值。這些方法標(biāo)記為可變的,因?yàn)樗鼈冃枰薷模ɑ蜣D(zhuǎn)換)結(jié)構(gòu)體的items數(shù)組。
上面所展現(xiàn)的IntStack類型只能用于Int值,不過,其對于定義一個(gè)泛型Stack類(可以處理任何類型值的棧)是非常有用的。
這里是一個(gè)相同代碼的泛型版本
// 這里是一個(gè)相同代碼的泛型版本:
struct Stack<T> {
var items = [T]()
mutating func push(item:T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
}
注意到Stack的泛型版本基本上和非泛型版本相同,但是泛型版本的占位類型參數(shù)為T代替了實(shí)際Int類型。這種類型參數(shù)包含在一對尖括號里(<T>),緊隨在結(jié)構(gòu)體名字后面。
T定義了一個(gè)名為“某種類型T”的節(jié)點(diǎn)提供給后來用。這種將來類型可以在結(jié)構(gòu)體的定義里任何地方表示為“T”。在這種情況下,T在如下三個(gè)地方被用作節(jié)點(diǎn):
- 創(chuàng)建一個(gè)名為items的屬性,使用空的T類型值數(shù)組對其進(jìn)行初始化;
- 指定一個(gè)包含一個(gè)參數(shù)名為item的push(_:)方法,該參數(shù)必須是T類型;
- 指定一個(gè)pop方法的返回值,該返回值將是一個(gè)T類型值。
由于Stack是泛型類型,所以在 Swift 中其可以用來創(chuàng)建任何有效類型的棧,這種方式如同Array和Dictionary。
你可以通過在尖括號里寫出棧中需要存儲的數(shù)據(jù)類型來創(chuàng)建并初始化一個(gè)Stack實(shí)例。比如,要創(chuàng)建一個(gè)strings的棧,你可以寫成Stack<String>():
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
// 現(xiàn)在棧已經(jīng)有4個(gè)string了
下圖將展示stackOfStrings如何push這四個(gè)值進(jìn)棧的過程:
從棧中pop并移除值"cuatro"
let fromTop = stackOfStrings.pop()
// fromTheTop 等于 "cuatro", 現(xiàn)在棧中還有3個(gè)string
下圖展示了如何從棧中pop一個(gè)值的過程:
擴(kuò)展一個(gè)泛型類型
當(dāng)你擴(kuò)展一個(gè)泛型類型的時(shí)候,你并不需要在擴(kuò)展的定義中提供類型參數(shù)列表。更加方便的是,原始類型定義中聲明的類型參數(shù)列表在擴(kuò)展里是可以使用的,并且這些來自原始類型中的參數(shù)名稱會被用作原始定義中類型參數(shù)的引用。
下面的例子擴(kuò)展了泛型Stack類型,為其添加了一個(gè)名為topItem的只讀計(jì)算屬性,它將會返回當(dāng)前棧頂端的元素而不會將其從棧中移除。
extension Stack {
var topItem:T? {
return items.isEmpty ? nil :items[items.count - 1]
}
}
if let topItem = stackOfStrings.topItem {
print("The top item on the stack is \(topItem).")
}
// 輸出 "The top item on the stack is tres."
泛型約束,為泛型類型添加約束
泛型約束大致分為以下幾種:
繼承約束,泛型類型必須是某個(gè)類的子類類型
協(xié)議約束,泛型類型必須遵循某些協(xié)議
條件約束,泛型類型必須滿足某種條件
約束的大概使用格式
// 繼承約束使用格式
func 函數(shù)名<泛型: 繼承父類>(參數(shù)列表) -> 返回值 {
// 函數(shù)體,泛型類型是某個(gè)類的子類類型
}
// 協(xié)議約束使用格式
func 函數(shù)名<泛型: 協(xié)議>(參數(shù)列表) -> 返回值 {
// 函數(shù)體,泛型類型遵循某些協(xié)議
}
// 條件約束使用格式
func 函數(shù)名<泛型1, 泛型2 where 條件>(參數(shù)列表) -> 返回值 {
// 函數(shù)體,泛型類型滿足某些條件
}
繼承約束使用范例
// 繼承約束使用范例
// 定義一個(gè)父類,動物類
class Animal {
// 動物都會跑
func run() {
print("Animal run")
}
}
class Dog :Animal {
override func run() { // 重寫父類方法
print("Dog run")
}
}
class Cat :Animal {
override func run() {
print("Cat run")
}
}
// 定義泛型函數(shù),接受一個(gè)泛型參數(shù),要求該泛型類型必須繼承Animal
func AnimalRunPint<T:Animal>(animal:T) {
animal.run() // 繼承了Animal類的子類都有run方法可以調(diào)用
}
AnimalRunPint(Dog()) // Dog run
AnimalRunPint(Cat()) // Cat run
協(xié)議約束使用范例
Swift標(biāo)準(zhǔn)庫中定義了一個(gè)Equatable協(xié)議,該協(xié)議要求任何遵循的類型實(shí)現(xiàn)等式符(==)和不等符(!=)對任何兩個(gè)該類型進(jìn)行比較。所有的Swift標(biāo)準(zhǔn)類型自動支持Equatable協(xié)議。
// 協(xié)議約束使用范例
// 定義泛型函數(shù),為泛型添加協(xié)議約束,泛型類型必須遵循Equatable協(xié)議
func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
var index = 0
for value in array {
if value == valueToFind { // 因?yàn)樽裱薊quatable協(xié)議,所以可以進(jìn)行相等比較
return index
} else {
index++
}
}
return nil
}
// 在浮點(diǎn)型數(shù)組中進(jìn)行查找,Double默認(rèn)遵循了Equatable協(xié)議
let doubleIndex = findIndex([3.14159, 0.1, 0.25], valueToFind: 9.3)
if let index = doubleIndex {
print("在浮點(diǎn)型數(shù)組中尋找到9.3,尋找索引為\(index)")
} else {
print("在浮點(diǎn)型數(shù)組中尋找不到9.3")
}
// 在字符串?dāng)?shù)組中進(jìn)行查找,String默認(rèn)遵循了Equatable協(xié)議
let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], valueToFind: "Andrea")
if let index = stringIndex {
print("在字符串?dāng)?shù)組中尋找到Andrea,尋找索引為\(index)")
} else {
print("在字符串?dāng)?shù)組中尋找不到Andrea")
}
/* 打印:
在浮點(diǎn)型數(shù)組中尋找不到9.3
在字符串?dāng)?shù)組中尋找到Andrea,尋找索引為2
*/
泛型協(xié)議和條件約束
上面的Equatable協(xié)議實(shí)際上不是普通的協(xié)議,而是泛型協(xié)議,假設(shè)泛型類型必須遵循一個(gè)協(xié)議,此時(shí)就必須在協(xié)議中引入一個(gè)關(guān)聯(lián)類型來解決。
// 定義一個(gè)泛型協(xié)議,和其他泛型使用方式不同,這里泛型是以關(guān)聯(lián)類型形式使用的
protocol Stackable{
// 聲明一個(gè)關(guān)聯(lián)類型,使用typealias關(guān)鍵字
typealias ItemType
mutating func push(item:ItemType)
mutating func pop() -> ItemType
}
struct Stack<T>:Stackable{
var store = [T]()
mutating func push(item:T){ // 實(shí)現(xiàn)協(xié)議的push方法要求
store.append(item)
}
mutating func pop() -> T { // 實(shí)現(xiàn)協(xié)議的pop方法要求
return store.removeLast()
}
}
// 創(chuàng)建Stack結(jié)構(gòu)體,泛型類型為String
var stackOne = Stack<String>()
stackOne.push("hello")
stackOne.push("swift")
stackOne.push("world")
let t = stackOne.pop()
print("t = \(t)") //結(jié)果:t = world
// 添加泛型條件約束,C1和C2必須遵循Stackable協(xié)議,而且C1和C2包含的泛型類型要一致
func pushItemOneToTwo<C1: Stackable, C2: Stackable
where C1.ItemType == C2.ItemType>(inout stackOne: C1, inout stackTwo: C2)
{ // 因?yàn)镃1和C2都遵循了Stackable協(xié)議,才有ItemType屬性可以調(diào)用
let item = stackOne.pop()
stackTwo.push(item)
}
// 定義另外一個(gè)結(jié)構(gòu)體類型,同樣實(shí)現(xiàn)Stackable協(xié)議,實(shí)際上里面的實(shí)現(xiàn)和Stack一樣
struct StackOther<T>: Stackable{
var store = [T]()
mutating func push(item:T){ // 實(shí)現(xiàn)協(xié)議的push方法要求
store.append(item)
}
mutating func pop() -> T { // 實(shí)現(xiàn)協(xié)議的pop方法要求
return store.removeLast()
}
}
// 創(chuàng)建StackOther結(jié)構(gòu)體,泛型類型為String
var stackTwo = StackOther<String>()
stackTwo.push("where")
// 雖然stackOne和stackTwo類型不一樣,但泛型類型一樣,也同樣遵循了Stackable協(xié)議
pushItemOneToTwo(&stackOne, stackTwo: &stackTwo )
print("stackOne = \(stackOne.store), stackTwo = \(stackTwo.store)")
// 打印:stackOne = ["hello"], stackTwo = ["where", "swift"]