gpt4 book ai didi

ios - 在 completionHandler 中使用泛型

转载 作者:搜寻专家 更新时间:2023-10-31 19:32:14 25 4
gpt4 key购买 nike

我有一个简单的应用程序,它使用自定义协议(protocol)通过 TCP 套接字与服务器通信。我想实现类似 HTTP 的响应请求行为,从套接字层抽象出来。所以我有简单的协议(protocol):

protocol ResponseType {
init(with frame: SocketMessage)
}

还有一些例子:

struct MessageAck: ResponseType {
var messageId: String
init(with frame: SocketMessage) {
messageId = frame.messageId
}
}

我创建了用于发送请求的简单协议(protocol):

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 泛型不是协变的(数组有特殊的硬编码异常(exception),涉及复制元素)。这意味着 Result<Apple>不是 Result<Fruit> 的子类型.参见 Swift Generics & Upcasting举例说明原因。

在您的情况下,什么会阻止您通过 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))
}
}
}
}

现在,而不是试图持有 T直接在callbacks ,它保存在每个单独的闭包中,对类(class)的其他人隐藏,并且 T永远不会逃脱这个功能。当你到达任何你打电话的地方callback在您的代码中,只需将 Result<SocketMessage> 传递给它我假设你已经在某个地方了。


旧答案:

解决您问题的最简单方法是让回调始终传递 Result<Data>并删除 T完全:

protocol APIClient {
func send(request: SocketAPIRequest, completion: ((Result<Data>) -> Void)?)
}

然后留给MessageAck (在完成处理程序中)从原始数据反序列化自身。

还有其他方法可以使用类型橡皮擦实现所有这些,但它们要复杂得多,有时甚至非常繁琐。

关于ios - 在 completionHandler 中使用泛型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49691595/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com