2016-08-12 79 views
3

我不得不在Swift中使用類型擦除幾次,但它總是涉及一個通用協議。在這種情況下,它涉及到泛型枚舉和泛型協議,我很難過。使用通用枚舉和通用協議的Swift類型擦除

這是我的泛型枚舉和通用的協議必要的擴展:

enum UIState<T> { 
    case Loading 
    case Success([T]) 
    case Failure(ErrorType) 
} 

protocol ModelsDelegate: class { 
    associatedtype Model 
    var state: UIState<[Model]> { get set } 
} 

extension ModelsDelegate { 

    func getNewState(state: UIState<[Model]>) -> UIState<[Model]> { 
     return state 
    } 

    func setNewState(models: UIState<[Model]>) { 
     state = models 
    } 
} 

這裏是我喜歡的類型擦除泛型類:

class AnyModelsDelegate<T>: ModelsDelegate { 
    var state: UIState<[T]> { 

     get { return _getNewState(UIState<[T]>) } // Error #1 
     set { _setNewState(newValue) } 
    } 

    private let _getNewState: ((UIState<[T]>) -> UIState<[T]>) 
    private let _setNewState: (UIState<[T]> -> Void) 

    required init<U: ModelsDelegate where U.Model == T>(_ models: U) { 
     _getNewState = models.getNewState 
     _setNewState = models.setNewState 
    } 
} 

,我發現了以下錯誤(他們是標記在代碼示例中):

錯誤#1:

Cannot convert value of type '(UIState<[T]>).Type' (aka 'UIState<Array<T>>.Type') to expected argument type 'UIState<[_]>' (aka 'UIState<Array<_>>')

我一直在研究這一段時間,並且在這段代碼中已經有了幾乎所有的「幾乎可行」的變體。該錯誤總是與吸氣劑有關。

+1

你傳遞一個類型,即採用的是 – dan

+0

I型的實例的方法'有點困惑爲什麼你的'getNewState'函數需要一個輸入,當然它應該是一個'() - > UIState <[Model]>'?雖然如果你的'getNewState'和'setNewState'函數只是爲了將獲取和設置轉發到你的類型擦除,它們是沒有必要的,因爲你可以直接在類型擦除中使用閉包(即'_getNewState = {models。狀態}'&'_setNewState = {models.state = $ 0}')。 – Hamish

+0

是的,我也有點困惑:)。它需要一個輸入,因爲最初我在沒有輸入時收到錯誤。讓我嘗試重構。 – damianesteban

回答

2

導致這個錯誤,因爲@dan has pointed out,問題是,在這條線你想傳遞一個類型作爲參數,而不是那個類型的實例:

get { return _getNewState(UIState<[T]>) } 

不過,我想首先質疑你對這個函數的一個參數的使用,當然一個函數應該沒有任何爭論?在這種情況下,你只是想你_getNewState功能有簽名() -> UIState<[T]>,並調用它像這樣:同樣

get { return _getNewState() } 

,如果在你的協議擴展您的getNewStatesetNewState(_:)功能只是爲了轉發讓存在和你的state屬性的類型擦除的設置 - 你可以讓他們擺脫完全簡化代碼,並在類型擦除的init改用封閉表達式:

_getNewState = { models.state } 
_setNewState = { models.state = $0 } 

(TH通過捕獲到models的說法,更多的信息參考ESE看到工作Closures: Capturing Values

最後,我懷疑你的意思是指UIState<T>而不是UIState<[T]>整個代碼,如T在這種情況下指的是元素您的.Success大小寫的數組作爲關聯值(除非您想在此處使用2D數組)。

總而言之,上述修改建議

,你會希望你的代碼看起來是這樣的:

enum UIState<T> { 
    case Loading 
    case Success([T]) 
    case Failure(ErrorType) 
} 

protocol ModelsDelegate: class { 
    associatedtype Model 
    var state: UIState<Model> { get set } 
} 

class AnyModelsDelegate<T>: ModelsDelegate { 
    var state: UIState<T> { 
     get { return _getNewState() } 
     set { _setNewState(newValue) } 
    } 

    private let _getNewState:() -> UIState<T> 
    private let _setNewState: (UIState<T>) -> Void 

    required init<U: ModelsDelegate where U.Model == T>(_ models: U) { 
     _getNewState = { models.state } 
     _setNewState = { models.state = $0 } 
    } 
} 
+0

優秀。謝謝。是的,我不想要一個2D數組。當我試圖解決錯誤時,這是​​我的錯誤。 – damianesteban

+0

@damianesteban高興地幫助:) – Hamish