【问题标题】:Swift Type Erasure with Generic Enum and Generic Protocol使用通用枚举和通用协议的 Swift 类型擦除
【发布时间】:2016-08-12 16:09:24
【问题描述】:

我不得不在 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&lt;[T]&gt;).Type' (aka 'UIState&lt;Array&lt;T&gt;&gt;.Type') to expected argument type 'UIState&lt;[_]&gt;' (aka 'UIState&lt;Array&lt;_&gt;&gt;')

我已经为此工作了一段时间,并且该代码有很多“几乎可以工作”的变体。错误总是与getter有关。

【问题讨论】:

  • 您将一个类型传递给一个采用该类型实例的方法
  • 我有点困惑,为什么你的getNewState 函数需要输入,肯定应该是() -&gt; UIState&lt;[Model]&gt;?虽然如果您的 getNewStatesetNewState 函数仅用于将获取和设置转发到您的类型擦除,它们不是必需的,因为您可以直接在带有闭包的类型擦除中执行此操作(即 _getNewState = { models.state } 和 @987654329 @)。
  • 是的,我对此也有点困惑 :)。它需要输入,因为最初我在没有输入时遇到错误。让我尝试重构。
  • 谢谢,就是这样。请发表您的答案。

标签: ios swift generics swift2 swift-protocols


【解决方案1】:

导致此错误的问题,如@dan has pointed out,是在这一行中,您尝试将类型作为参数传递,而不是该类型的实例:

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

但是,我首先会质疑您对该函数的参数的使用,当然,获取函数应该根本没有参数吗?在这种情况下,您只需要您的 _getNewState 函数具有签名 () -&gt; UIState&lt;[T]&gt;,并像这样调用它:

get { return _getNewState() }

此外,如果您的协议扩展中的 getNewStatesetNewState(_:) 函数仅存在于将您的 state 属性的获取和设置转发到类型擦除 - 您可以通过摆脱它们完全并在类型擦除的init 中使用闭包表达式:

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

(这些通过捕获对models 参数的引用来工作,有关更多信息,请参阅Closures: Capturing Values

最后,我怀疑您的意思是在整个代码中引用 UIState&lt;T&gt; 而不是 UIState&lt;[T]&gt;,因为在这种情况下,T 指的是您的 .Success 案例作为关联值的数组中的一个元素(除非你想要一个二维数组)。

总而言之,通过上述建议的更改,您会希望您的代码看起来像这样:

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 }
    }
}

【讨论】:

  • 优秀。谢谢你。是的,我不想要一个二维数组。当我试图解决这些错误时,这是​​我的一个错误。
  • @damianesteban 乐于助人:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多