发布者完成后,从AnyCancellable数组中删除

时间:2020-03-14 18:06:37

标签: swift combine

是否有一种好的方法来处理AnyCancellable数组以在完成/取消后删除存储的AnyCancellable

说我有这个

import Combine
import Foundation

class Foo {

    private var cancellables = [AnyCancellable]()

    func startSomeTask() -> Future<Void, Never> {
        Future<Void, Never> { promise in
            DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
                promise(.success(()))
            }
        }
    }

    func taskCaller() {
        startSomeTask()
            .sink { print("Do your stuff") }
            .store(in: &cancellables)
    }

}

每次调用taskCaller时,都会创建一个AnyCancellable并将其存储在数组中。 我想在阵列完成时从阵列中删除该实例,以避免浪费内存。

我知道我可以做类似的事情,而不是数组

var taskCancellable: AnyCancellable?

并通过以下操作存储可取消的内容:

taskCancellable = startSomeTask().sink { print("Do your stuff") }

但这将结束创建多个单个可取消的操作,并可能污染代码。我不要像这样的课程

class Bar {

    private var task1: AnyCancellable?
    private var task2: AnyCancellable?
    private var task3: AnyCancellable?
    private var task4: AnyCancellable?
    private var task5: AnyCancellable?
    private var task6: AnyCancellable?

}

2 个答案:

答案 0 :(得分:2)

这是一个好主意,但实际上没有什么要删除的。当完成(完成或取消)在管道上进行时,管道上的所有内容都将以良好的顺序取消订阅,所有类(Subscription对象)都将被释放,依此类推。因此,在Future发出价值或失败后,唯一仍然有意义的“运行”是管道末端的接收器,它很小。

要查看此内容,请运行以下代码

for _ in 1...100 {
    self.taskCaller()
}

并使用工具来跟踪您的分配。果然,此后有100个AnyCancellable对象,总计3KB。没有期货; startSomeTask中分配的其他所有对象都不存在,而且它们很小(48字节),以至于它们没有关系。

答案 1 :(得分:2)

我在开发一个应用程序时问了自己同样的问题,该应用程序会生成大量最终存储在同一数组中的可取消项。对于长期存在的应用,数组大小可能会变得很大。

即使内存占用很小,它们仍然是对象,它们消耗堆,这会导致堆碎片及时。

我找到的解决方案是在发布者完成后删除可取消:

func consumePublisher() {
    var cancellable: AnyCancellable!
    cancellable = makePublisher()
        .sink(receiveCompletion: { [weak self] _ in self?.cancellables.remove(cancellable) },
              receiveValue: { doSomeWork() })
    cancellable.store(in: &cancellables)
}

确实,代码不是那么漂亮,但至少没有内存浪费:)

一些高阶函数可用于使该模式可在同一类的其他地方重用:

func cleanupCompletion<T>(_ cancellable: AnyCancellable) -> (Subscribers.Completion<T>) -> Void {
    return { [weak self] _ in self?.cancellables.remove(cancellable) }
}

func consumePublisher() {
    var cancellable: AnyCancellable!
    cancellable = makePublisher()
        .sink(receiveCompletion: cleanupCompletion(cancellable),
              receiveValue: { doSomeWork() })
    cancellable.store(in: &cancellables)
}

或者,如果您需要支持以完成工作:

func cleanupCompletion<T>(_ cancellable: AnyCancellable) -> (Subscribers.Completion<T>) -> Void {
        return { [weak self] _ in self?.cancellables.remove(cancellable) }
    }
    
func cleanupCompletion<T>(_ cancellable: AnyCancellable, completionWorker: @escaping (Subscribers.Completion<T>) -> Void) -> (Subscribers.Completion<T>) -> Void {
    return { [weak self] in
        self?.cancellables.remove(cancellable)
       completionWorker($0)
    }
}

func consumePublisher() {
    var cancellable: AnyCancellable!
    cancellable = makePublisher()
        .sink(receiveCompletion: cleanupCompletion(cancellable) { doCompletionWork() },
              receiveValue: { doSomeWork() })
    cancellable.store(in: &cancellables)
}