swift iOS13的合并流不会在使用调度程序的运算符之后流动

fdx2calv  于 2022-11-21  发布在  Swift
关注(0)|答案(3)|浏览(109)

iOS13的发布者的合并流似乎没有在使用调度程序的操作员之后流动。
下面是我的代码:

import Foundation
import Combine

struct MyPublisher: Publisher {
    typealias Output = Int
    typealias Failure = Error

    func receive<S>(subscriber: S) where S : Subscriber,
        Failure == S.Failure,
        Output == S.Input {
            subscriber.receive(1)
            print("called 1")
            subscriber.receive(2)
            print("called 2")
            subscriber.receive(completion: .finished)
            print("called finish")
    }
}

MyPublisher()
//    .receive(on: RunLoop.main) // If this line removed, it will be fine.
//    .throttle(for: .milliseconds(1000), scheduler: RunLoop.main, latest: false)) // If this line removed, it will be fine.
//    .debounce(for: .milliseconds(1000), scheduler: RunLoop.main)) // If this line removed, it will be fine.
//    .delay(for: .milliseconds(1000), scheduler: DispatchQueue.main)) // If this line removed, it will be fine.
    .print()
    .sink(receiveCompletion: { completion in
        switch completion {
        case .finished:
            print("finished")
        case .failure(let error):
            print("error:\(error)")
        }
    }, receiveValue: { num in
        print("\(num)")
    })

我期望输出是

1
2
finished

但实际输出却什么也没有。
如果我不使用receivethrottledebouncedelay,输出将是好的。
是bug还是我的代码有问题?
我尝试使用Playground(Xcode 11 beta3)。

sqyvllje

sqyvllje1#

订阅:

我不知道为什么它在单线程的情况下工作,但是你应该确保在subscriber上调用received(subscription:)。如果你不需要处理订阅者的要求,你可以使用Subscribers.empty

struct MyPublisher: Publisher {

    typealias Output = Int
    typealias Failure = Never

    func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
        subscriber.receive(subscription: Subscriptions.empty)
        _ = subscriber.receive(1)
        Swift.print("called 1")
        _ = subscriber.receive(2)
        Swift.print("called 2")
        _ = subscriber.receive(completion: .finished)
        Swift.print("called finish")
    }
}

任何可取消的:

您应该注意到一个警告:
调用"sink(receiveCompletion:receiveValue:)“的结果未使用
由于sink会传回AnyCancellable,因此应该会出现此消息:

func sink(receiveCompletion: @escaping ((Subscribers.Completion<Self.Failure>) -> Void), receiveValue: @escaping ((Self.Output) -> Void)) -> AnyCancellable

一旦AnyCancellable被释放,任何返回AnyCancellable的操作都将被取消。
我的推测是,如果你把它放在另一个线程上,那么当到达调用方法的结尾时,可取消的将在接收订阅之前释放。但是当在当前线程上接收时,它似乎正好在订阅和输出显示的时间执行。最有可能的是,当当前线程退出时,可取消的正在被释放。

zaq34kh6

zaq34kh62#

使用可取消

例如:

class ImageLoader: ObservableObject {
    @Published var image: UIImage?
    private var cancellable: AnyCancellable?

func fetchImages() {
        guard let urlString  = urlString,
            let url =  URL(string: urlString) else { return }
        cancellable = URLSession.shared.dataTaskPublisher(for: url)
            .map { UIImage(data: $0.data) }
            .replaceError(with: nil)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] in self?.image = $0 }
    }
}
dvtswwa3

dvtswwa33#

使用下划线

你可以传递下划线来传递警告,我已经用了Naishta回答的例子。
例如

class ImageLoader: ObservableObject {
    @Published var image: UIImage?
    

    func fetchImages() {
        guard let urlString  = urlString,
            let url =  URL(string: urlString) else { return }
        _ = URLSession.shared.dataTaskPublisher(for: url)
            .map { UIImage(data: $0.data) }
            .replaceError(with: nil)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] in self?.image = $0 }
    }
}

相关问题