Downcast Generic AnyObject to Protocol Associated Type Self.Model

Rah*_*iya 11 generics ios swift swift-protocols

我正在开发一个Restofire库,我想在其中保留一个配置对象.我想在配置对象中有一个ResponseSerializer,但事情是ResponseSerializer是一个通用的.

public struct Configuration<M> {

    /// The Default `Configuration`. 
    static let defaultConfiguration = Configuration<AnyObject>()

    /// The base URL. `nil` by default.
    public var baseURL: String!

    /// The `ResponseSerializer`
    public var responseSerializer: ResponseSerializer<M, NSError> = AlamofireUtils.JSONResponseSerializer()

    /// The logging, if enabled prints the debug textual representation of the 
    /// request when the response is recieved. `false` by default.
    public var logging: Bool = false

}
Run Code Online (Sandbox Code Playgroud)

我用baseUrl设置了defaultConfiguration Configuration.defaultConfiguration.baseUrl = "http://httpbin.org/"

我有一个带有associatedType要求的协议,它使用defaultConfiguration作为默认实现.但我需要将通用AnyObject更改为associatedType模型,以便配置对象的responseSerializer返回类型Model.

public protocol Configurable {

    associatedtype Model

    /// The Restofire configuration.
    var configuration: Configuration<Model> { get }

}

public extension Configurable {

    /// `Restofire.defaultConfiguration`
    // Cannot convert return expression of Configuration<AnyObject> to return type Configuration <Self.Model>
    public var configuration: Configuration<Model> {
        return Restofire.defaultConfiguration
    }

}
Run Code Online (Sandbox Code Playgroud)

我收到了错误 Cannot convert return expression of Configuration<AnyObject> to return type Configuration <Self.Model>

我怎样才能使用Model而不是AnyObject?

我还有一个继承自Configurable的协议Requestable

public protocol Requestable: Configurable {

    /// The type of object returned in response.
    associatedtype Model

    /// The base URL.
    var baseURL: String { get }

    /// The path relative to base URL.
    var path: String { get }

    /// The request parameters.
    var parameters: AnyObject? { get }

    /// The logging.
    var logging: Bool { get }

    /// The Response Serializer
    var responseSerializer: ResponseSerializer<Model, NSError> { get  }
}

// MARK: - Default Implementation
public extension Requestable {

    /// `configuration.BaseURL`
    public var baseURL: String {
        return configuration.baseURL
    }

    /// `nil`
    public var parameters: AnyObject? {
        return nil
    }

    /// `configuration.logging`
    public var logging: Bool {
        return configuration.logging
    }

    /// `configuration.responseSerializer`
    var responseSerializer: ResponseSerializer<Model, NSError> {
         return configuration.responseSerializer
    } 

}
Run Code Online (Sandbox Code Playgroud)

RealCode at https://github.com/Restofire/Restofire/compare/Add/DefaultResponseSerializer

我可以直接在下面做,但用户将无法使用配置对象进行设置.

// MARK: - Default Implementation
public extension Requestable {

    /// `configuration.responseSerializer`
    var responseSerializer: ResponseSerializer<Model, NSError> {
         return AlamofireUtils.JSONResponseSerializer()
    } 

}
Run Code Online (Sandbox Code Playgroud)

还有其他方法吗?

Ant*_*Dev 2

你想做的事是不可能的。这是因为 Swift 中的强类型安全系统。

看起来您想将 限制configuration为关联的 type Model,但您还想提供默认配置。这里的问题是您的默认配置不能保证其泛型类型与Model.

Swift 类型系统不会让你<AnyObject>作为 type传递<Model>,因为它无法确保对象实际上是该类型Model

当您使某些内容符合Configurable并声明 的类型时Model,您是在说:“我的配置必须使用我给出的类型。” 不能defaultConfiguration保证这一点,所以不能以这种方式使用。

responseSerializer通过查看您的代码,您可以使用它来确定要使用的类型。但是,如果您使用不同的Configurable对象,它们都需要不同的responseSerializers,因此它们不能都使用默认配置。

我花了很多时间思考这个问题,但我没有找到任何解决办法。你将不得不以某种方式改变你的设计。

如果你将其转移responseSerializer到协议上Configurable,那么你就可以使其成为Configuration非通用的。在这种情况下,您将能够responseSerializer在 上的协议扩展中创建Configurable. 如果您需要使用responseSerializer独立于对象的配置Configurable,那么您必须在responseSerializer<AnyObject, NSError>使用它的任何地方创建一个。我不熟悉您的图书馆的整个设计和意图,所以我不确定这是否适合您想要实现的目标。

我可以肯定地告诉您的一件事是,您的设计必须更改才能使用defaultConfiguration.