2016-08-12 43 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

相关问题