足球资料库数据/孙祥/nba五佳球/足球直播哪个平台好 - cctv5今日现场直播

首頁 > 知識庫 > 正文

當 Swift 中的協議遇到泛型
2016-01-30 18:13:17   來源: mengyidan1988   評論:0 點擊:

Swift 中的協議如果需要泛型化,可以通過抽象類型成員的方式實現,而不是在參數類型上做文章。至此,協議本身可以不再被當成是一個類型,而是看做一個通用的約束。英文原文 問題 如果你曾將一個泛型協議當做類型使用: protocol GenericProtocol { typealias AbstractType func magic() -> AbstractType
Swift 中的協議如果需要泛型化,可以通過抽象類型成員的方式實現,而不是在參數類型上做文章。至此,協議本身可以不再被當成是一個類型,而是看做一個通用的約束。英文原文

問題
如果你曾將一個泛型協議當做類型使用:
protocol GenericProtocol {      typealias AbstractType    func magic() -> AbstractType}let list : [GenericProtocol] = []  

很快 Xcode 會給你報個錯:
引用

Protocol 'GenericProtocol' can only be used as a generic constraint because it has Self or associated type requirements. 

如果你不熟悉泛型,可能會奇怪,為什么泛型協議不能當做類型使用呢?答案很簡單:泛型協議的名稱,即:GenericProtocol 表示一組類型,并不是一個單一類型。比如你有一個關于 GenericProtocol 的隨機數組,并不能確定每個元素的 magic() 方法返回的類型到底是什么,因為數組中每個元素可能都是不同的。

這個問題在 StackOverflow 也有很好的討論

類型成員 VS 參數化
在 Swift 中,泛型化協議可以通過使用抽象類型成員來達成(使用關鍵字 typealias),而類、結構體、方法和函數則通過使用類型參數化來達成泛型。(觀察下面的例子)
func genericFunc<T>(ts : [T]) -> Bool {      return true; // not very exciting}

事實證明,一般情況下,只要任何語言同時支持這兩種表達方式,那么類型成員和參數化最后產生的效果是一樣的。我們可以想象 Swift 在未來的迭代中支持通過參數化來達成泛型,那么下面的代碼可以這么寫:
protocol GenericProtocolWithParam<T> {      func magic() -> T}let list : [GenericProtocolWithParam<String>] = []  

如果你對類型參數化和抽象類型成員之間的權衡利弊感興趣,請看官方開發者論壇的這個帖子,以及 Scala 是如何處理這個問題的。

解決方法
我們可以使用 thunk 來解決在協議中缺少類型參數化的問題。
  • 首先定義一個結構體,該結構體實現了協議的所有方法。
  • 在具體的實現方法中,再轉發給(調用)『實現協議的抽象類型』。
  • 在結構體的初始化過程中,這個實現了協議的抽象類型會被當做參數傳入(依賴注射)

在實際的操作中,我們都是通過閉包來完成的:
引用

維基百科指出:一個 thunk 通常是一個子程序,被創造出來自動地,協助調用其他的子程序。阮一峰也有篇介紹文章

protocol GenericProtocol {      typealias AbstractType    func magic() -> AbstractType}struct GenericProtocolThunk<T> : GenericProtocol {      // closure which will be used to implement `magic()` as declared in the protocol    private let _magic : () -> T    // `T` is effectively a handle for `AbstractType` in the protocol    init<P : GenericProtocol where P.AbstractType == T>(_ dep : P) {        // requires Swift 2, otherwise create explicit closure        _magic = dep.magic    }    func magic() -> T {        // any protocol methods are implemented by forwarding        return _magic()    }

一旦我們擁有一個 thunk,我們可以把他當做類型使用(需要我們自己提供具體的類型)
struct StringMagic : GenericProtocol {      typealias AbstractType = String    func magic() -> String {        return "Magic!"    }}// we can now create arrays of thunks if we specify the type paramlet magicians : [GenericProtocolThunk<String>] = [GenericProtocolThunk(StringMagic())]  magicians.first!.magic() // returns "Magic!"  

是否需要 Self
在協議的上下文聲明中,Self 表示遵守協議的類型。這使得協議更加通用,self 可以被看做是針對便利類型成員的方法糖。例如:
protocol EquatableSelf {      func equals(other : Self) -> Bool}// By adopting `EquatableSelf`, every occurrence of `Self` in the protocol gets// semantically replaced with `ImplicitStruct`struct ImplicitStruct : EquatableSelf {      var val : Int64    func equals(other: ImplicitStruct) -> Bool {        return self.val == other.val;    }}

如果我們沒有能力使用 Self,可以采用下面的方式達到同樣的目的:
protocol EquatableTypealias {      typealias EquatableType    func equals(other : EquatableType) -> Bool}struct ExplicitStruct : EquatableTypealias {      typealias EquatableType = ExplicitStruct    var val : Int64    func equals(other: ExplicitStruct) -> Bool {        return self.val == other.val;    }}

面向協議編程
協議在 Swift 中扮演中流砥柱的角色,正如 Dave Abrahams 在 WWDC 2015 上 Session 408 中說的那樣:
引用
... When we made Swift, we made the first protocol-oriented programming language.

如果你對 Swift 中的面向協議編程感興趣,那么我們強烈推薦去觀摩完整視頻。尤其下面這張幻燈片很好地闡述了『何時需要添加 Self』:



泛型和類型變異
引入泛型而來的一個問題就是泛型類型的變異(variance)。簡單的說,他涉及以下問題:
class Animal {      // Animal-specific ivars + methods}class Cat : Animal {      // Cat-specific ivars + methods}var cats : [Cat] = [Cat()]// Here, we're typecasting from [Cat] to [Animal].// Such typecasts could be unsafe depending on the details.var animals : [Animal] = cats  

我希望以后能有機會和你們探討這個問題,建議先從維基百科這個頁面 入手了解一下類型變異(主要指類型的協變和逆變 covariance-and-contravariance)
引用
SwiftGG 也有一篇關于逆變和協變不錯的科普文,通過此文我們可以知道 Swift 中的泛型是「不變的(Invariance)」
結論:optionals,arrays,dictionaries 在 Swift 中都是協變(covariant)的,而泛型以及自定義的類型都是不變的(Invariance),function 則視具體情況而定


題圖:『When Harry Met Sally』:)

本文轉自:http://chengway.in/

相關熱詞搜索:java Swift 編程 mobile 移動開發

上一篇:一套交互設計工具推薦
下一篇:Swoole-1.8.0版本已發布,新增多項新特性

分享到: 收藏