Downcast Generic AnyObject to Protocol Associated Type Self.Model

时间:2016-04-25 08:35:10

标签: ios swift generics 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

}

我使用baseUrl Configuration.defaultConfiguration.baseUrl = "http://httpbin.org/"

设置defaultConfiguration

我有一个带有associatedType要求的协议,它使用defaultConfiguration作为默认实现。但是我需要将通用AnyObject更改为associatedType Model,以便配置对象的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
    }

}

我收到错误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
    } 

}

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

的RealCode

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

// MARK: - Default Implementation
public extension Requestable {

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

}

还有其他办法吗?

1 个答案:

答案 0 :(得分:1)

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

您希望将configuration限制为关联类型Model,但您还希望提供默认配置。这里的问题是您的默认配置无法保证其泛型类型与Model的类型相同。

Swift类型系统不允许您将<AnyObject>作为<Model>类型传递,因为它无法确保该对象实际上属于Model类型。< / p>

当你做出符合Configurable的内容并声明Model的类型时,你会说,&#34;我的配置必须使用我给出的类型。&#34 ; defaultConfiguration无法保证,因此无法以这种方式使用。

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

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

如果您将responseSerializer移至Configurable协议,则可以使Configuration非通用。在这种情况下,您可以在responseSerializer上的协议扩展中创建Configurable。如果您需要使用responseSerializer配置独立于Configurable对象,那么无论您在何处使用它,都必须创建responseSerializer<AnyObject, NSError>。我不熟悉您图书馆的整个设计和意图,所以我不确定这是否适用于您想要实现的目标。

我可以确定地告诉你的一件事是你的设计必须改变才能使用defaultConfiguration