我有一个简单的应用程序,它使用自定义协议通过TCP套接字与服务器通信。我想实现类似HTTP的响应请求行为,从套接字层抽象出来。 所以我有简单的协议:
protocol ResponseType {
init(with frame: SocketMessage)
}
还有一些例子:
struct MessageAck: ResponseType {
var messageId: String
init(with frame: SocketMessage) {
messageId = frame.messageId
}
}
我创建了用于发送请求的简单协议:
protocol APIClient {
func send<T: ResponseType>(request: SocketAPIRequest, completion: ((Result<T>) -> Void)?)
}
enum SocketAPIRequest {
case textMessage(messageId: String, ...)
...
}
最后:
enum Result<T> {
case success(T)
case failure(Error)
}
class SocketAPIClient: APIClient {
typealias MessageId = String
private var callbacks = [Receipt: ((Result<ResponseType>) -> Void)]()
...
func send<T>(request: SocketAPIRequest, completion: ((Result<T>) -> Void)?) where T : ResponseType {
....
callbacks[stompFrame.receiptId] = completion
....
}
}
因此,当我想存储每个请求的回调时,在收到应答后调用它,我得到了这样的错误:
Cannot assign value of type '((Result<T>) -> Void)?' to type '((Result<ResponseType>) -> Void)?'
我想混合类型和对象的问题,或者其他问题。
Swift 泛型不是协变的(Array 有特殊的硬编码例外,涉及复制元素)。这意味着Result<Apple>
不是Result<Fruit>
的子类型。参见 Swift Generics & Upcast 了解原因的示例。
在您的情况下,什么会阻止您将Result<MessageBody>
传递给预期Result<MessageAck>
的回调?例如:
for callback in callbacks {
callback(result)
}
你怎么知道这对任何给定类型的result
在编译时都是合法的?
编辑(更好的答案):
您可以将类型隐藏在闭包中以获得所需的内容。试试这个:
class SocketAPIClient: APIClient {
typealias MessageId = String
private var callbacks = [Receipt: ((Result<SocketMessage>) -> Void)]() // <--- Change
func send<T>(request: SocketAPIRequest, completion: ((Result<T>) -> Void)?) where T : ResponseType {
// Store the closure we don't understand inside a closure we do
callbacks[stompFrame.receiptId] = { result in
switch result {
case .success(let message):
completion?(.success(T.init(with: message)))
case .failure(let error):
completion?(.failure(error))
}
}
}
}
现在,它不再试图直接在callbacks
中持有T
,而是在每个单独的闭包中持有,对类的其他成员隐藏,并且T
永远不会逃脱此功能。当你到达代码中调用callback
的任何位置时,只需将其传递给我假设您已经在某处拥有的Result<SocketMessage>
。
旧答案:
解决问题的最简单方法是让回调始终传递Result<Data>
并完全删除T
:
protocol APIClient {
func send(request: SocketAPIRequest, completion: ((Result<Data>) -> Void)?)
}
然后将其留给MessageAck
(在完成处理程序中)从原始数据中反序列化自身。
还有其他方法可以使用类型橡皮擦实现所有这些,但它们要复杂得多,有时甚至非常繁琐。
您是否尝试过以下签名
func send<T:ResponseType>(request: SocketAPIRequest, completion: ((Result<T>) -> Void)?){ ... }
仍然收到错误?
编辑1:或者也许你应该尝试这样的事情
protocol APIClient {
associatedtype T
func send(request: SocketAPIRequest, completion: ((Result<T>) -> Void)?)
}
和
class SocketAPIClient: APIClient {
typealias MessageId = String
typealias T = ResponseType
private var callbacks = [Receipt: ((Result<ResponseType>) -> Void)]()
...
func send(request: SocketAPIRequest, completion: ((Result<T>) -> Void)?) {
....
callbacks[stompFrame.receiptId] = completion
....
}
}