Swift泛型

前情提要

Swift的泛型側(cè)重于將類型作為一種變量或者占位符來使用。

為什么要用泛型呢,就是方便。

比如上一篇文章中的用到的一個類:

類定義:

open class UICollectionViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType> : NSObject, UICollectionViewDataSource where SectionIdentifierType : Hashable, ItemIdentifierType : Hashable {

...}

SectionIdentifierType和ItemIdentifierType只要是可hash的就可以,無論你是字符串還是數(shù)字等等。提供了極大地靈活性。



正文內(nèi)容

1.泛型函數(shù)的定義:

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

? ? let tempA = a

? ? a = b

? ? b = tempA? ??

}

這個T就是類型形式參數(shù),一旦指定了T,就可以用它定義函數(shù)的形參(a和b),也可以用它做函數(shù)返回值類型,或者函數(shù)體中的類型標注。在不同的實際情況下,T會被傳入?yún)?shù)的實際類型替換(int,string...)。

使用注意:類型形式參數(shù)的名字要有描述性,比如SectionIdentifierType,ItemIdentifierType。

2.泛型類的定義:

class?UICollectionViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType>:??NSObject, UICollectionViewDataSource?where?SectionIdentifierType : Hashable, ItemIdentifierType : Hashable {

...}

當擴展一個泛型類時,不需要重新提供類型形式參數(shù)(SectionIdentifierType,ItemIdentifierType)

例:

extension?UICollectionViewDiffableDataSource {

...

可以直接使用SectionIdentifierType,ItemIdentifierType

}

注意:當Struct或enum使用泛型時,若要改變Struct中的變量,記得使用 mutating 關(guān)鍵字修飾方法。

3.類型約束:

有時在泛型用于泛型函數(shù)(1)和泛型類(2)上,會強制其遵守特定的類型約束。類型約束指出一個類型形式參數(shù)(比如上面T)必須繼承自特定類,或者遵循一個特定的協(xié)議,組合協(xié)議。比如Swift的Dictionary。

帶約束的泛型函數(shù)的定義:

func someFunc<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {

...}

4.關(guān)聯(lián)類型(protocol中使用的泛型):

關(guān)聯(lián)類型可以給協(xié)議中用到的類型一個占位符名稱,直到采納協(xié)議時,才指定用于該關(guān)聯(lián)類型的實際類型。通過associatedtype關(guān)鍵字指定。是Swift為協(xié)議指定泛型的一種方式。

例:

一:ItemType(關(guān)聯(lián)類型)類型數(shù)據(jù)的容器協(xié)議

protocol Container {

? ? associatedtype ItemType

? ? mutating func append(_ item: ItemType)

? ? var count: Int { get }

? ? subscript(i: Int) -> ItemType { get }

}

遵循協(xié)議的實現(xiàn):

(1)struct IntStack: Container {

? ? ...

? ? typealieas ItemType = Int

? ? ...

}

(2)struct Stack<Element>: Container {

? ? ...

}

這個不用再寫?typealieas ItemType = Int,因為Swift會利用類型推斷為ItemType賦值,就是Element的類型。

二:添加約束(Equatable)的ItemType(關(guān)聯(lián)類型)類型數(shù)據(jù)的容器協(xié)議

protocol Container {

associatedtype ItemType: Equatable

mutating func append(_ item: ItemType)

var count: Int { get }

subscript(i: Int) -> ItemType { get }

}

三:可自身后綴的容器

protocol SuffixableContainer: Container {

? ? associatedType Suffix:?SuffixableContainer where Suffix.ItemType == ItemType

? ? func suffix(_ size: Int) -> Suffix

}

4.where子句:

例:

func allItemsMatch<C1: Container, C2: Container>(_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.Item == C2.Item, C1.Item: Equatable {

? ? ...

}

擴展一個使用泛型的類時,也可以對其泛型類型進行約束,以UICollectionViewDiffableDataSource為例

extension?UICollectionViewDiffableDataSource where?SectionIdentifierType: Equatable {

? ? ...

}

5.泛型下標:

extension Container {

? ? subscript<Indices: Sequenece>(indices: Indices) -> [Item] where Indices.Iterator.Element == Int {

? ? ? ? var result = [Item]()

? ? ? ? for index in indices {

? ? ? ? ? ? ? ? result.append(self[index])

????????????}

? ? ? ? ? ? ? ? return result

????}

}

約束了傳入?yún)?shù)的單元數(shù)據(jù)類型只能是Int,別的就出錯了。



結(jié)尾

泛型思維:

1.面向過程的編程,可以將常用代碼段封裝在一個函數(shù)中,然后通過函數(shù)調(diào)用來達到目標代碼重用的目的。面向?qū)ο蟮姆椒?,則可以通過類的繼承來實現(xiàn)代碼的重用;

2.但,如果要寫一個可用于不同數(shù)據(jù)類型的算法,面向過程要對源碼進行復(fù)制和修改,生成不同數(shù)據(jù)類型版本的算法函數(shù),調(diào)用時需要對數(shù)據(jù)類型進行手工判斷;面向?qū)ο罂梢酝ㄟ^函數(shù)重載來實現(xiàn)。

3.而,泛型編程可以做到源代碼級別的重用,抽象程度更高;

? ? ? ? -編寫以類 類型作為參數(shù)的一個模板函數(shù),在調(diào)用時再將參數(shù)實例化為具體的數(shù)據(jù)類型;

? ? ? ? -Swift中的protocol,extension protocol..會分擔泛型編程/模板編程的抽象化壓力;

4.總結(jié): 泛型(generic)編程是一種面向算法的多態(tài)技術(shù),泛型編程研究對軟件組件的系統(tǒng)化組織。目標是推出一種針對算法,數(shù)據(jù)結(jié)構(gòu)和內(nèi)存分配機制的分類方法。以及其他能夠帶來高度可重用性,模塊化和可用性的軟件工具。

??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容