LakaLe_
LakaLe_

Reputation: 454

Memory leak when using `Publishers.Sequence`

I have a function that create collection of Publishers:

func publishers(from text: String) -> [AnyPublisher<SignalToken, Never>] {
     let signalTokens: [SignalToken] = translate(from: text)
     var delay: Int = 0
     let signalPublishers: [AnyPublisher<SignalToken, Never>] = signalTokens.map { token in
         let publisher = Just(token)
                           .delay(for: .milliseconds(delay), scheduler: DispatchQueue.main)
                           .eraseToAnyPublisher()
         delay += token.delay
         return publisher
     }
     return signalPublishers
}

In service class I have to method, one for play():

func play(signal: String) {
     anyCancellable = signalTokenSubject.sink(receiveValue: { token in print(token) }

     anyCancellable2 = publishers(from: signal)
         .publisher
         .flatMap { $0 }
         .subscribe(on: DispatchQueue.global())
         .sink(receiveValue: { [weak self] token in
            self?.signalTokenSubject.send(token)
         })
}

and one for stop():

func stop() {
     anyCancellable?.cancel()
     anyCancellable2?.cancel()
}

I've had problem with memory. When collection of publishers is large and I stop() before whole Publishers.Sequence is .finshed memory increase and never release.

Is there a way to completed Publishers.Sequence earlier, before Combine iterate over whole collection?

Upvotes: 0

Views: 327

Answers (1)

matt
matt

Reputation: 534903

To reclaim the memory, release the pipelines:

func stop() {
     anyCancellable?.cancel()
     anyCancellable2?.cancel()
     anyCancellable = nil 
     anyCancellable2 = nil 
}

Actually you don't need the cancel calls, because releasing the pipelines does cancel in good order; that is the whole point of AnyCancellable. So you can just say:

func stop() {
     anyCancellable = nil 
     anyCancellable2 = nil 
}

Another thing to note is that you are running all your publishers at once. The sequence does not arrive sequentially; the whole sequence is dumped into the flapMap which starts all the publishers publishing simultaneously. Thus cancelling doesn't do you all that much good. You might want to set the maxPublishers: on your flatMap so that backpressure prevents more than some small number of publishers from arriving simultaneously (like for example one at a time).

Upvotes: 1

Related Questions