亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Swift > 正文

Swift如何使用類型擦除及自定義詳解

2020-03-09 17:31:20
字體:
來源:轉載
供稿:網友

前言

在 Swift 的世界中,如果我們將協議稱之為國王,那么泛型則可以視作皇后,所謂一山不容二虎,當我們把這兩者結合起來使用的時候,似乎會遇到極大的困難。那么是否有一種方法,能夠將這兩個概念結合在一起,以便讓它們成為我們前進道路上的墊腳石,而不是礙手礙腳的呢?答案是有的,這里我們將會使用到類型擦除 (Type Erasure) 這個強大的特性。

你也許曾聽過類型擦除,甚至也使用過標準庫提供的類型擦除類型如 AnySequence。但到底什么是類型擦除? 如何自定義類型擦除? 在這篇文章中,我將討論如何使用類型擦除以及如何自定義。在此感謝 Lorenzo Boaro 提出這個主題。

有時你想對外部調用者隱藏某個類的具體類型,或是一些實現細節。在一些情況下,這樣做能防止靜態類型在項目中濫用,或者保證了類型間的交互。類型擦除就是移除某個類的具體類型使其變得更通用的過程。

協議或抽象父類可作為類型擦除簡單的實現方式之一。例如 NSString 就是一個例子,每次創建一個 NSString 實例時,這個對象并不是一個普通的 NSString 對象,它通常是某個具體的子類的實例,這個子類一般是私有的,同時這些細節通常是被隱藏起來的。你可以使用子類提供的功能而不用知道它具體的類型,你也沒必要將你的代碼與它們的具體類型聯系起來。

在處理 Swift 泛型以及關聯類型協議的時候,可能需要使用一些高級的內容。Swift 不允許把協議當做具體的類型來使用。例如,如果你想編寫一個方法,它的參數是一個包含了 Int 的序列,那么下面這種做法是不正確的:

func f(seq: Sequence<Int>) { ...

你不能這樣使用協議類型,這樣會在編譯時報錯。但你可以使用泛型來替代協議,解決這個問題:

func f<S: Sequence>(seq: S) where S.Element == Int { ...

有時候這樣寫完全可以,但有些地方還存在一些比較麻煩的情況,通常你不可能只在一個地方添加泛型: 一個泛型函數對其他泛型要求更多… 更糟糕的是,你不能將泛型作為返回值或者屬性。這就跟我們想的有點不一樣了。

func g<S: Sequence>() -> S where S.Element == Int { ...

我們希望函數 g 能返回任何符合的類型,但上面這個不同,它允許調用者選擇他所需要的類型,然后函數 g 來提供一個合適的值。

Swift 標準庫中提供了 AnySequence 來幫助我們解決這個問題。AnySequence 包裝了一個任意類型的序列,并擦除了它的類型。使用 AnySequence 來訪問這個序列,我們來重寫一下函數 f 與 函數 g:

func f(seq: AnySequence<Int>) { ...func g() -> AnySequence<Int> { ...

泛型部分不見了,同時具體的類型也被隱藏起來了。由于使用了 AnySequence 包裝具體的值,它帶來了一定的代碼復雜性以及運行時間成本。但是代碼卻更簡潔了。

Swift 標準庫中提供了很多這樣的類型,如 AnyCollection、AnyHashable 及 AnyIndex。這些類型在你自定義泛型或協議的時候非常的管用,你也可以直接使用這些類型來簡化你的代碼。接下來讓我們探索實現類型擦除的多種方式吧。

基于類的類型擦除

有時我們需要在不暴露類型信息的情況下從多個類型中包裝一些公共的功能,這聽起來就像是父類-子類的關系。事實上我們的確可以使用抽象父類來實現類型擦除。父類提供 API 接口,不用去管誰來實現。而子類根據具體的類型信息實現相應的功能。

接下來我們將使用這種方式來自定義 AnySequence,我們將其命名為 MAnySequence:

class MAnySequence<Element>: Sequence {

這個類需要一個 iterator 類型作為 makeIterator 返回類型。我們必須要做兩次類型擦除來隱藏底層的序列類型以及迭代器的類型。我們在 MAnySequence 內部定義了一個 Iterator 類,該類遵循著 IteratorProtocol 協議,并在 next() 方法中使用 fatalError 拋出異常。Swift 本身不支持抽象類型,但這樣也夠了:

class Iterator: IteratorProtocol { func next() -> Element? { fatalError("Must override next()") }}

MAnySequence 對 makeIterator 方法實現也差不多。直接調用將拋出異常,這用來提示子類需要重寫這個方法:

 func makeIterator() -> Iterator { fatalError("Must override makeIterator()") }}

這樣就定義了一個基于類的類型擦除的API,私有的子類將來實現這些API。公共類通過元素類型參數化,但私有實現類由它包裝的序列類型進行參數化:

private class MAnySequenceImpl<Seq: Sequence>: MAnySequence<Seq.Element> {

MAnySequenceImpl 需要一個繼承于 Iterator 的子類:

class IteratorImpl: Iterator {

IteratorImpl 包裝了序列的迭代器:

var wrapped: Seq.Iteratorinit(_ wrapped: Seq.Iterator) { self.wrapped = wrapped}

在 next 方法中調用被包裝的序列迭代器:

 override func next() -> Seq.Element? { return wrapped.next() }}

相似地,MAnySequenceImpl 包裝一個序列:

var seq: Seqinit(_ seq: Seq) { self.seq = seq}

從序列中獲取迭代器,然后將迭代器包裝成 IteratorImpl 對象返回,這樣就實現了 makeIterator 的功能。

 override func makeIterator() -> IteratorImpl { return IteratorImpl(seq.makeIterator()) }}

我們需要一種方法來實際創建這些東西:對 MAnySequence 添加一個靜態方法,該方法創建一個 MAnySequenceImpl 實例,并將其作為 MAnySequence 類型返回給調用者。

extension MAnySequence { static func make<Seq: Sequence>(_ seq: Seq) -> MAnySequence<Element> where Seq.Element == Element { return MAnySequenceImpl<Seq>(seq) }}

在實際開發中,我們可能會做一些額外的操作來讓 MAnySequence 提供一個初始化方法。

我們來試試 MAnySequence:

func printInts(_ seq: MAnySequence<Int>) { for elt in seq { print(elt) }}let array = [1, 2, 3, 4, 5]printInts(MAnySequence.make(array))printInts(MAnySequence.make(array[1 ..< 4]))

完美!

基于函數的類型擦除

有時我們希望對外暴露支持多種類型的方法,但又不想指定具體的類型。一個簡單的辦法就是,存儲那些簽名僅涉及到我們想公開的類型的函數,函數主體在底層已知具體實現類型的上下文中創建。

我們一起看看如何運用這種方法來設計 MAnySequence,與前面的實現很類似。它是一個結構體而非類,這是因為它僅僅作為容器使用,不需要有任何的繼承關系。

struct MAnySequence<Element>: Sequence {

跟之前一樣,MAnySequence 也需要一個可返回的迭代器(Iterator)。迭代器同樣被設計為結構體,并持有一個參數為空并返回 Element? 的存儲型屬性,實際上這個屬性是一個函數,被用于 IteratorProtocol 協議的 next 方法中。接下來 Iterator 遵循 IteratorProtocol 協議,并在 next 方法中調用函數:

struct Iterator: IteratorProtocol { let _next: () -> Element? func next() -> Element? {  return _next() }}

MAnySequence 與 Iterator 很相似:持有一個參數為空返回 Iterator 類型的存儲型屬性。遵循 Sequence 協議并在 makeIterator 方法中調用這個屬性。

let _makeIterator: () -> Iteratorfunc makeIterator() -> Iterator { return _makeIterator()}

MAnySequence 的構造函數正是魔法起作用的地方,它接收任意序列作為參數:

init<Seq: Sequence>(_ seq: Seq) where Seq.Element == Element {

接下來需要在構造函數中包裝此序列的功能:

_makeIterator = {

如何生成迭代器?請求 Seq 序列生成:

var iterator = seq.makeIterator()

接下來我們利用自定義的迭代結構體包裝序列生成的迭代器,包裝后的 _next 屬性將會在迭代器協議的 next() 方法中被調用:

   return Iterator(_next: { iterator.next() })  } }}

接下來展示如何使用 MAnySequence:

func printInts(_ seq: MAnySequence<Int>) { for elt in seq {  print(elt) }}let array = [1, 2, 3, 4, 5]printInts(MAnySequence(array))printInts(MAnySequence(array[1 ..< 4]))

正確運行,太棒了!

當需要將小部分功能包裝為更大類型的一部分時,這種基于函數的類型擦除方法特別實用,這樣做就不需要有單獨的類來實現被擦除類型的這部分功能了。

比方說你現在想要編寫一些適用于各種集合類型的代碼,但它真正需要能夠對這些集合執行的操作是獲取計數并執行從零開始的整數下標。如訪問 tableView 數據源。它可能看起來像這樣:

class GenericDataSource<Element> { let count: () -> Int let getElement: (Int) -> Element init<C: Collection>(_ c: C) where C.Element == Element,C.Index == Int {  count = { c.count }  getElement = { c[$0 - c.startIndex] } }}

GenericDataSource 其他代碼可通過調用 count() 或 getElement() 來操作傳入的集合。且不會讓集合類型破壞 GenericDataSource 泛型參數。

結束語

類型擦除是一種非常有用的技術,它可用來阻止泛型對代碼的侵入,也可用來保證接口簡單明了。通過將底層類型包裝起來,將API與具體的功能進行拆分。這可以通過使用抽象的公共超類和私有子類或將 API 包裝在函數中來實現。對于只需要一些功能的簡單情況,基于函數類型擦除極其有效。

Swift 標準庫提供了幾種可直接利用的類型擦除類型。如 AnySequence 包裝一個 Sequence,正如其名,AnySequence 允許你對序列迭代而無需知道序列具體的類型。AnyIterator 也是類型擦除的類型,它提供一個類型擦除的迭代器。AnyHashable 也同樣是類型擦除的類型,它提供了對Hashable類型訪問功能。Swift 還有很多基于集合的擦除類型,你可以通過搜索 Any 來查閱。標準庫中也為 Codable API 設計了類型擦除類型: KeyedEncodingContainer 和 KeyedDecodingContainer。它們都是容器協議類型包裝器,可用來在不知道底層具體類型信息的情況下實現 Encode 和 Decode。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。

作者:Mike Ash,原文鏈接,原文日期:2017-12-18

譯者:rsenjoyer;校對:Yousanflics,numbbbbb;定稿:Forelax


注:相關教程知識閱讀請移步到swift教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美黄色成人网| 亚洲欧美日本精品| 日韩国产高清污视频在线观看| 97在线视频观看| 日韩亚洲第一页| 亚洲成人性视频| 欧美日韩免费区域视频在线观看| 欧美性极品少妇精品网站| 狠狠久久五月精品中文字幕| 2019国产精品自在线拍国产不卡| 26uuu另类亚洲欧美日本老年| 亚洲精品乱码久久久久久金桔影视| 成人免费视频在线观看超级碰| 亚洲一区二区三区久久| 97久久伊人激情网| 欧美第一黄色网| 日韩av免费网站| 欧美自拍视频在线| 欧美巨乳在线观看| 日韩黄色在线免费观看| 久久久免费在线观看| 亚洲午夜久久久久久久| 欧美国产激情18| 久久艹在线视频| 久久精品国产成人| 国产亚洲精品91在线| 亚洲欧美色图片| 91九色视频导航| 亚洲一区二区三区香蕉| 一区二区三区高清国产| 国产欧美一区二区三区四区| 一区二区三区高清国产| 亚洲色无码播放| 国产亚洲精品久久久久久| 欧美xxxx18性欧美| 国产欧美精品一区二区| 亚洲丁香久久久| 亚洲免费中文字幕| 成人av在线亚洲| www高清在线视频日韩欧美| 欧美中文字幕第一页| 26uuu另类亚洲欧美日本一| 亚洲欧美第一页| 日韩av电影在线免费播放| 国产精品久久久久久久午夜| 欧美裸身视频免费观看| 91精品国产自产在线观看永久| 国产成人极品视频| 欧美日韩国产精品一区二区不卡中文| 久久精品视频va| 欧洲精品在线视频| 日韩精品视频免费在线观看| 黑人巨大精品欧美一区二区免费| 成人午夜在线视频一区| 福利一区福利二区微拍刺激| 久久国产精品电影| 一区二区三区国产在线观看| 亚洲人永久免费| 主播福利视频一区| 欧美在线视频一区二区| 在线观看国产成人av片| 性欧美办公室18xxxxhd| 黑人巨大精品欧美一区二区三区| 日韩av免费一区| 97在线看免费观看视频在线观看| 国产精品久久久久久久久久东京| 亚洲老头老太hd| 欧美人在线视频| 91精品国产色综合久久不卡98| 精品国产乱码久久久久久婷婷| 久久久这里只有精品视频| 国产专区欧美专区| 久久精品91久久香蕉加勒比| 夜夜嗨av色综合久久久综合网| 亚洲伊人一本大道中文字幕| 国产精品久久久久久av下载红粉| 国产欧美日韩最新| 精品亚洲精品福利线在观看| 欧美成人精品一区二区三区| 91免费看片在线| 国产一区二区三区毛片| 国自产精品手机在线观看视频| 国产精品综合不卡av| 欧美一级视频在线观看| 久久久中精品2020中文| 亚洲热线99精品视频| 欧美黑人性视频| 国自在线精品视频| 亚洲精品电影久久久| 久久久免费观看| 深夜成人在线观看| 欧美老肥婆性猛交视频| 久久久成人的性感天堂| 国产精品亚洲自拍| 在线播放国产一区二区三区| 亚洲色图五月天| 欧美丰满少妇xxxx| 国产亚洲精品久久久久久| 国产精品视频白浆免费视频| 国产精品伦子伦免费视频| 大伊人狠狠躁夜夜躁av一区| 日韩av网站在线| 久久精品国产亚洲精品| 亚洲第一二三四五区| 国产精品青草久久久久福利99| 国产精品一区二区在线| 久久精品99无色码中文字幕| 久久久久久久成人| 欧美成人精品激情在线观看| 热久久免费视频精品| 欧美精品第一页在线播放| 欧美日韩一区二区免费视频| 国内成人精品一区| 亚洲国产天堂网精品网站| 国产亚洲成精品久久| 国产美女精品视频| 最近中文字幕mv在线一区二区三区四区| 久久精品中文字幕免费mv| 国产91ⅴ在线精品免费观看| 国产成人自拍视频在线观看| 日韩av片永久免费网站| 国产午夜精品理论片a级探花| 色综合天天狠天天透天天伊人| 国模极品一区二区三区| 岛国av午夜精品| 激情成人在线视频| 国模gogo一区二区大胆私拍| 国产精品入口免费视频一| 日本一区二区三区四区视频| 久久精品国产久精国产一老狼| 97国产成人精品视频| 亚洲欧美在线看| www.亚洲免费视频| 国产精品天天狠天天看| 激情成人在线视频| 国产精品久久久久久久7电影| 亚洲天堂成人在线视频| 亚洲人成欧美中文字幕| 欧美另类第一页| 国产精国产精品| 久久人人97超碰精品888| 91免费高清视频| 欧美性视频精品| 成人午夜一级二级三级| 色999日韩欧美国产| 91精品久久久久久久久| 日韩精品视频免费在线观看| 欧美一级高清免费| 亚洲成av人影院在线观看| 久久人人97超碰精品888| 亚洲丁香久久久| 欧美丝袜美女中出在线| 亚洲xxxx妇黄裸体| 亚洲精品www久久久久久广东| 人人澡人人澡人人看欧美| 91亚洲精品在线观看| 亚洲一二在线观看| 精品国产欧美一区二区三区成人| 久久青草精品视频免费观看| 欧美亚洲国产视频小说| 最近日韩中文字幕中文| 欧美日韩一区二区三区在线免费观看| 亚洲欧美日韩视频一区| 国产精品成人一区二区三区吃奶|