gpt4 book ai didi

swift - 如何在Swift中正确使用EventLoopF​​uture?

转载 作者:行者123 更新时间:2023-12-02 12:12:00 24 4
gpt4 key购买 nike

我对 EventLoop futures 和 Promise 很陌生。我的软件堆栈:

  • Go + gRPC 后端
  • Swift + SwiftUI + GRPC + NIO 中的 iOS 客户端

我有一些工作要做,并正在寻找有关如何改进它的建议,因为我对 .map.flatMap.always

以下是 iOS 应用程序中我的 gRPC 数据单例的相关函数:

import Foundation
import NIO
import GRPC

class DataRepository {
static let shared = DataRepository()
// skip ...

func readItem(id: Int64, eventLoop: EventLoop) -> EventLoopFuture<V1_ReadResponse> {
// TODO: Is this the right place?
defer {
try? eventLoop.syncShutdownGracefully()
}

let promise = eventLoop.makePromise(of: V1_ReadResponse.self)

var request = V1_ReadRequest()
request.api = "v1"
request.id = id

let call = client.read(request, callOptions: callOptions) // client - GRPCClient initialized in the singleton

call.response.whenSuccess{ response in
return promise.succeed(response)
}

call.response.whenFailure{ error in
return(promise.fail(error))
}

return promise.futureResult
}

我在 SwiftUI View 中的代码:

import SwiftUI
import NIO

struct MyView : View {
@State private var itemTitle = "None"

var body: some View {
Text(itemTitle)
}

func getItem() {
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let result = DataRepository.shared.readItem(id: 1, eventLoop: eventLoopGroup.next())

_ = result.always { (response: Result<V1_ReadResponse, Error>) in

let res = try? response.get()
if let resExist = res {
self.itemTitle = resExist.item.title
}

_ = response.mapError{ (err: Error) -> Error in
print("[Error] Connection error or item not found: \(err)")
return err
}
}
}

我应该重构 getItem 和/或 readItem 吗?有什么具体建议吗?

最佳答案

我有一个非常具体的建议,然后是一些一般性的建议。第一个也是最具体的建议是,如果您不编写 NIO 代码,则可能不需要创建 EventLoop根本没有。 grpc-swift 将创建自己的事件循环,您可以简单地使用它们进行回调管理。

这可以让您重构您的 readItem代码简单地是:

func readItem(id: Int64, eventLoop: EventLoop) -> EventLoopFuture<V1_ReadResponse> {
var request = V1_ReadRequest()
request.api = "v1"
request.id = id

let call = client.read(request, callOptions: callOptions) // client - GRPCClient initialized in the singleton
return call.response
}

这极大地简化了您的代码,让您可以将管理事件循环的所有复杂工作基本上推迟到 grpc-swift,这样您就可以担心您的应用程序代码了。

否则,这里有一些一般性建议:

不要关闭不属于您的事件循环

位于 readItem 的顶部你有这个代码:

// TODO: Is this the right place?
defer {
try? eventLoop.syncShutdownGracefully()
}

您可以看到我在上面的示例中删除了它。那是因为这不是合适的地方。事件循环是长期存在的对象:它们的构建和关闭成本很高,因此您很少需要这样做。一般来说,您希望事件循环由相当高级的对象(如 AppDelegate 或某些高级 View Controller 或 View )构建和拥有。然后它们将被系统中的其他组件“借用”。对于您的特定应用程序,正如我所指出的,您可能希望事件循环由 grpc-swift 拥有,因此您不应该关闭任何事件循环,但一般来说,如果您采取此策略,则适用一个明确的规则:如果您没有创建 EventLoop ,不要关闭它。它不是你的,你只是借用它。

事实上,在 NIO 2.5.0 中,NIO 团队made it an error以这种方式关闭事件循环:如果您替换 try?try!您会看到应用程序崩溃。

EventLoopGroups 是顶级对象

在您的 MyView.getItem 中函数您创建一个 MultiThreadedEventLoopGroup 。我上面的建议是,您根本不要创建自己的事件循环,但如果您要这样做,这不是一个好地方。

对于 SwiftUI,最好的办法就是让你的 EventLoopGroup成为 EnvironmentObjectAppDelegate 注入(inject)到 View 层次结构中。每个需要 EventLoopGroup 的 View 然后可以安排从环境中提取一个事件循环,这允许您在应用程序中的所有 View 之间共享事件循环。

线程安全

EventLoopGroup使用自己的线程的私有(private)池来执行回调和应用程序工作。在 getItem您可以从这些 future 回调之一修改 View 状态,而不是从主队列。

您应该使用DispatchQueue.main.async { }在修改状态之前重新加入主队列。您可能想将其包装在一个辅助函数中,如下所示:

extension EventLoopFuture {
func always<T>(queue: DispatchQueue, _ body: (Result<T>) -> Void) {
return self.always { result in
queue.async { body(result) }
}
}
}

回调重构

作为生活质量的一个小问题,这段代码可以重构:

let res = try? response.get()                
if let resExist = res {
self.itemTitle = resExist.item.title
}

_ = response.mapError{ (err: Error) -> Error in
print("[Error] Connection error or item not found: \(err)")
return err
}

对此:

switch response {
case .success(let response):
self.itemTitle = response.item.title
case .failure(let err):
print("[Error] Connection error or item not found: \(err)")
}

关于swift - 如何在Swift中正确使用EventLoopF​​uture?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59119038/

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