Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift 为什么“publisher.Map”热衷于消费上游价值?_Swift_Combine_Backpressure - Fatal编程技术网

Swift 为什么“publisher.Map”热衷于消费上游价值?

Swift 为什么“publisher.Map”热衷于消费上游价值?,swift,combine,backpressure,Swift,Combine,Backpressure,假设我有一个自定义订阅服务器,它在订阅时请求一个值,然后在收到前一个值后三秒钟请求一个附加值: class MySubscriber: Subscriber { typealias Input = Int typealias Failure = Never private var subscription: Subscription? func receive(subscription: Subscription) { print("Subsc

假设我有一个自定义订阅服务器,它在订阅时请求一个值,然后在收到前一个值后三秒钟请求一个附加值:

class MySubscriber: Subscriber {
    typealias Input = Int
    typealias Failure = Never

    private var subscription: Subscription?

    func receive(subscription: Subscription) {
        print("Subscribed")

        self.subscription = subscription
        subscription.request(.max(1))
    }

    func receive(_ input: Int) -> Subscribers.Demand {
        print("Value: \(input)")

        DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) {
            self.subscription?.request(.max(1))
        }

        return .none
    }

    func receive(completion: Subscribers.Completion<Never>) {
        print("Complete")
        subscription = nil
    }
}
但是如果我添加了
map
操作符,那么
MySubscriber
甚至从未收到订阅
map
似乎已同步请求了
Demand.Unlimited
在收到订阅后,应用程序会无限旋转,因为
map
尝试耗尽无限范围:

(1...).publisher
    .map { value in
        print("Map: \(value)")
        return value * 2
    }
    .subscribe(MySubscriber())

// The `map` transform is executed infinitely with no delay:
//
//     Map: 1
//     Map: 2
//     Map: 3
//     ...
我的问题是,
map
为什么会这样?我希望
map
将其下游需求传递给上游。因为
map
应该是用于转换而不是副作用,所以我不理解它当前行为的用例是什么

编辑 我实现了一个版本的map,以显示我认为它应该如何工作:

extension Publishers {
    struct MapLazily<Upstream: Publisher, Output>: Publisher {
        typealias Failure = Upstream.Failure

        let upstream: Upstream
        let transform: (Upstream.Output) -> Output

        init(upstream: Upstream, transform: @escaping (Upstream.Output) -> Output) {
            self.upstream = upstream
            self.transform = transform
        }

        public func receive<S: Subscriber>(subscriber: S) where S.Input == Output, S.Failure == Upstream.Failure {
            let mapSubscriber = Subscribers.LazyMapSubscriber(downstream: subscriber, transform: transform)
            upstream.receive(subscriber: mapSubscriber)
        }
    }
}

extension Subscribers {
    class LazyMapSubscriber<Input, DownstreamSubscriber: Subscriber>: Subscriber {
        let downstream: DownstreamSubscriber
        let transform: (Input) -> DownstreamSubscriber.Input

        init(downstream: DownstreamSubscriber, transform: @escaping (Input) -> DownstreamSubscriber.Input) {
            self.downstream = downstream
            self.transform = transform
        }

        func receive(subscription: Subscription) {
            downstream.receive(subscription: subscription)
        }

        func receive(_ input: Input) -> Subscribers.Demand {
            downstream.receive(transform(input))
        }

        func receive(completion: Subscribers.Completion<DownstreamSubscriber.Failure>) {
            downstream.receive(completion: completion)
        }
    }
}

extension Publisher {
    func mapLazily<Transformed>(transform: @escaping (Output) -> Transformed) -> AnyPublisher<Transformed, Failure> {
        Publishers.MapLazily(upstream: self, transform: transform).eraseToAnyPublisher()
    }
}
我的猜测是,为
发布者定义的
map
的超负荷。Sequence
使用某种快捷方式来提高性能。这对于无限序列来说是中断的,但即使对于有限序列,不管下游需求如何,都会急切地耗尽序列,这会扰乱我的直觉。在我看来,以下代码:

(1...3).publisher
    .map { value in
        print("Map: \(value)")
        return value * 2
    }
    .subscribe(MySubscriber())
应打印:

Subscribed
Map: 1
Value: 2
Map: 2
Value: 4
Map: 3
Value: 6
Complete
而是打印:

Map: 1
Map: 2
Map: 3
Subscribed
Value: 2
Value: 4
Value: 6
Complete

下面是一个简单的测试,它不涉及任何自定义订阅者:

(1...).publisher
    //.map { $0 }
    .flatMap(maxPublishers: .max(1)) {
        (i:Int) -> AnyPublisher<Int,Never> in
        Just<Int>(i)
            .delay(for: 3, scheduler: DispatchQueue.main)
            .eraseToAnyPublisher()
}
.sink { print($0) }
.store(in: &storage)

果然,它解决了这个问题!通过对map操作符隐藏序列发布器,我们可以防止优化。

您使用的是什么Xcode和什么目标平台?在macOS 10.15.4上使用Xcode 11.4,我无法重现您的问题。使用您的
MySubscriber
,以下管道运行正常:
(1…3).publisher.print(“Empty/map”).map{dump($0)}.print(“map/subscriber”).subscribe(MySubscriber())
哦,抱歉,我刚刚意识到我复制了错误的代码。我尝试使用无限范围
1…
,而不是
1…3
(我在测试时将其设为有限范围)。我刚刚更新了要修复的问题。为了回答您的问题,我仍然使用10.14.6版本的Xcode 11.3.1。我刚刚在运行macOS 10.15.2和Xcode 11.4的个人MacBook上进行了测试,我看到了相同的行为。我开始以不同的方式编写一个反例,并最终确认了您的结果!好,提交:
FB7660502
。我希望你不介意我在报告中使用了你的简单示例。相反,我对此非常高兴。嘿,我想到了一个简单的解决方法。将其添加到答案中。它证实了你的假设。另外,这里有一个挂起的非常简短的测试用例:
(1…).publisher.map{$0}.prefix(1).sink{{uuu}
@robmayoff我想可能是publisher.Sequence是一个只用于测试的玩具。
Map: 1
Map: 2
Map: 3
Subscribed
Value: 2
Value: 4
Value: 6
Complete
(1...).publisher
    //.map { $0 }
    .flatMap(maxPublishers: .max(1)) {
        (i:Int) -> AnyPublisher<Int,Never> in
        Just<Int>(i)
            .delay(for: 3, scheduler: DispatchQueue.main)
            .eraseToAnyPublisher()
}
.sink { print($0) }
.store(in: &storage)
(1...).publisher.eraseToAnyPublisher()
    .map { $0 }
    // ...