在 completionHandler 中使用泛型



我有一个简单的应用程序,它使用自定义协议通过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
....
}
}

相关内容

  • 没有找到相关文章

最新更新