Swift:为符合通用协议的通用类创建包装器类时出错

时间:2019-06-27 10:24:27

标签: swift generics swift-protocols type-erasure

我想为符合通用协议的通用类创建Wrapper类,但是由于某些原因,我无法使其正常工作。

背后的想法是将包装 AnyNetworkRequest 用作应用程序中的Erased-Type,因此无需像 _NetworkRequest 中那样定义泛型类型。

我看不到 AnyNetworkRequest 上缺少/错误的内容。如果有什么可以指出我的缺失或错误之处,我将不胜感激。

// Protocol with associatedtypes

public protocol NetworkRequest {

    associatedtype RequestSerializationType: RequestSerializationProtocol
    associatedtype RequestResponseType: NetworkResponseProtocol

    var requestSerializer: RequestSerializationType { get }
    var requestResponse: RequestResponseType? { get }
}


// Generic Request

public class _NetworkRequest<RequestSerializationType: RequestSerializationProtocol, RequestResponseType: NetworkResponseProtocol>: NetworkRequest {

    fileprivate init() {}

    public lazy var requestSerializer: RequestSerializationType = { RequestSerializationType.init() }()
    public var requestResponse: RequestResponseType?
}

// Concrete Request

public class DataNetworkRequest: _NetworkRequest<ConcreteHTTPRequestSerializer, ConcreteDataNetworkResponse> {}

// Concrete Request

public class JSONDataNetworkRequest: _NetworkRequest<ConcreteJSONRequestSerializer, ConcreteJSONDataNetworkResponse> {}


// Type Erased Wrapper
// Cannot make this wrapper work
// Error 1: Type 'AnyNetworkRequest' does not conform to protocol 'NetworkRequest'
// Error 2: Reference to invalid associated type 'RequestSerializationType' of type 'AnyNetworkRequest'

public class AnyNetworkRequest : NetworkRequest { //E1

    private let request : _NetworkRequest<RequestSerializationType, RequestResponseType> //E2

    init<T: NetworkRequest>(_ networkRequest: T) where T.RequestSerializationType == RequestSerializationType, T.RequestResponseType == RequestResponseType {
        request = networkRequest
    }
}

编辑:第一次修改

// Protocol with associatedtypes

public protocol NetworkRequest {

    associatedtype RequestSerializationType: RequestSerializationProtocol
    associatedtype RequestResponseType: NetworkResponseProtocol

    var requestSerializer: RequestSerializationType { get }
    var requestResponse: RequestResponseType? { get }
}


// Generic Request

public class _NetworkRequest<RST: RequestSerializationProtocol, RRT: NetworkResponseProtocol>: NetworkRequest {

    public typealias RequestSerializationType = RST
    public typealias RequestResponseType = RRT


    fileprivate init() {}

    public lazy var requestSerializer: RequestSerializationType = { RequestSerializationType.init() }()
    public var requestResponse: RequestResponseType?
}

// Concrete Request

public class DataNetworkRequest: _NetworkRequest<ConcreteHTTPRequestSerializer, ConcreteDataNetworkResponse> {}

// Concrete Request

public class JSONDataNetworkRequest: _NetworkRequest<ConcreteJSONRequestSerializer, ConcreteJSONDataNetworkResponse> {}


// Type Erased Wrapper
// Cannot make this wrapper work
// Error 1: Type 'AnyNetworkRequest' does not conform to protocol 'NetworkRequest'
// Error 2: Reference to invalid associated type 'RequestSerializationType' of type 'AnyNetworkRequest'

public class AnyNetworkRequest : NetworkRequest { //E1

    /*  // E1 forces me to include typealiases
        public typealias RequestSerializationType = <#type#>
        public typealias RequestResponseType = <#type#>
    */

    private let request : _NetworkRequest<RequestSerializationType, RequestResponseType>

    var requestSerializer: RequestSerializationType { //E2
        return request.requestSerializer
    }

    var requestResponse: RequestResponseType? {
        return request.requestResponse
    }

    init<T: NetworkRequest>(_ networkRequest: T) where T.RST == RequestSerializationType, T.RRT == RequestResponseType {
        request = networkRequest
    }
}

2 个答案:

答案 0 :(得分:1)

错误很容易解释。

1)您的AnyNetworkRequest类确实不符合NetworkRequest协议。顺便说一下,我不知道为什么要这么做。 requestSerializerrequestResponse属性以及所需的类型别名不满足要求。通常,您可以将其重写如下:

public class AnyNetworkRequest: NetworkRequest {

    var requestSerializer: RequestSerializationType { 
       return request.requestSerializer
    }

    var requestResponse: RequestResponseType? { 
       return request.requestResponse
    }

    private let request : _NetworkRequest<RequestSerializationType, RequestResponseType>

   // ...
}

但是我们需要

2)需要为关联类型指定一些内容的地方。您不能在通用声明中使用RequestSerializationTypeRequestResponseType,因为它们不是具体类型。

因此,您不能以这种方式执行类型擦除。

除了输入更少的字母外,我不知道为什么您需要在这里摆脱泛型,但是我可以提供的是使用类型别名:

typealias DataNetworkRequest = _NetworkRequest<ConcreteHTTPRequestSerializer, ConcreteDataNetworkResponse>

typealias JSONDataNetworkRequest = _NetworkRequest<ConcreteJSONRequestSerializer, ConcreteJSONDataNetworkResponse>

这样,您就可以避免多余的继承,并且可以更清楚地表达类型,以防万一是您的目标。

在这种情况下,完全不需要

AnyNetworkRequest

答案 1 :(得分:1)

这实际上不是类型擦除器的工作方式-类型擦除器的作用是允许异构世界中的同类接口,在异构世界中,许多类型可以符合感兴趣的协议。

AnySequence为例,它的界面与此类似:

struct AnySequence<Element>: Sequence {
    init<S>(_ sequence: S) where S == Sequence, S.Element == Element
}

AnySequence删除原始序列类型,而不是Element类型。对于您而言,您无法摆脱两种关联的类型,类型擦除器只能隐藏符合NetworkRequest的实际类。 AnyRequest仍需要有关这两种类型的信息。