为什么 Swift 的 Combine 的地图返回 Publishers.Map<Self, T> 而不是 <T, Error>



我的请求导致AnyPublisher

let foo: AnyPublisher<API.Response, API.Error> = APIClient.get(API.Endpoints.Demo)

现在我想将这个发布者的值映射到字符串数组,API中的message。响应类型是[String : [String]],所以我的尝试是

foo.map { $0.message.map { $0.key } }

在这一点上,我希望我最终会与AnyPublisher<[String], API.Error>,或只是任何其他出版商,但参数化为<[String], API.Error>
而不是我最终与Publishers.Map<AnyPublisher<API.Response, API.Error>, [String]>

func map<T>(_ transform: @escaping (Self.Output) -> T) -> Publishers.Map<Self, T>

上面的.map函数的文档是正确的,但为什么它是这样工作的-为什么有人期望映射保持以前的出版商类型,但以某种方式嵌套在那里。我实在想不明白,请帮助我,谢谢:)

如果您习惯了其他reactivex风格的库,或者您习惯了对象总是堆分配的语言(如Java或Kotlin或Scala),那么组合类型可能会相当令人震惊。

由于Publishers.Map对其上游发布器的类型(在本例中为AnyPublisher<API.Response, API.Error>)是泛型的,因此它可以内联存储其上游发布器,而不需要将上游封装在存在的容器中,也不需要为上游堆分配存储空间。

Map还优化了map操作符的连续使用。Combine框架为所有Publisher定义了一个map操作符,Map继承了它。但是Map还定义了一个更特殊的map运算符,它将连续的map运算符合并在一起。

下面是一个例子:

import Combine
let foo: AnyPublisher<[String: [String]], Error> = Empty().eraseToAnyPublisher()
let chained = foo
.map { Array($0.keys) }
.filter { !$0.isEmpty }
.map { $0.joined(separator: ", ") }
.map { "[($0)]" }
print("chained", type(of: chained))

chained的类型是什么?这是

Map<
Filter<
Map<
AnyPublisher<
Dictionary<String, Array<String>>,
Error
>,
Array<String>
>
>,
String
>

注意这里的两件事。尽管我使用了三次map运算符,但该类型中只有两个Map。这是因为map的最终使用使用了Map类型的专用map运算符,该运算符将连续两次使用的map合并为单个Map实例。

由于MapFilterstructs,它们可以存储在堆栈中,也可以作为其他数据类型的属性直接内联。因此,chained中的值可以存储在堆栈中。

一般来说,Map可能需要在堆上存储它的transform函数,但它也不需要执行堆分配来存储它的上游。在上面的例子中,由于传递给mapfilter的参数都没有关闭任何外部变量,Swift可能能够优化出所有

创建chained

值时的堆分配。在这里使用泛型类型还可以让编译器更多地了解chained值是如何构造的,因此它可能能够执行其他优化。

因此,对发布者应用操作符通常会返回不同类型的发布者。如果您需要编写一个以发布者为参数的函数,这意味着您应该使该函数在发布者的类型上具有泛型。例如,每个SwiftUIView都有一个onReceive修饰符,签名如下:

func onReceive<P>(
_ publisher: P,
perform action: @escaping (P.Output) -> Void
) -> some View where P : Publisher, P.Failure == Never

您可以将任何发布者传递给onReceive,只要该发布者的Failure关联类型是Never。因此,您可以将MapFilterAnyPublisher传递给onReceive

相关内容

  • 没有找到相关文章

最新更新