Yogurt
Yogurt

Reputation: 3045

Nil'ing AnyCancellable not cancelling subscription

From the documentation

An AnyCancellable instance automatically calls cancel() when deinitialized.

Yet in the following code

    var cancellable: AnyCancellable?

    let subject: PassthroughSubject<Int, Never>? = PassthroughSubject<Int, Never>()

    cancellable = subject?.sink(receiveValue: {
        print("-> sending to cancellable \($0)")
    })

    print("send 1")
    subject?.send(1)


    // documentation states "An AnyCancellable instance automatically calls cancel() when deinitialized."
    print("cancellable nil'd")
    cancellable = nil

    print("send 2")
    subject?.send(2)

    print("send 3")
    subject?.send(3)

    DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
        print("done")
    }

/*
send 1
-> sending to cancellable 1
cancellable nil'd
send 2
-> sending to cancellable 2
send 3
-> sending to cancellable 3
done
*/

Shows that nil'ing cancellable does not stop the subscriber from getting values.

While using a Set and removing all or nil'ing the set will stop the subscriptions. I even tried throwing everything into an autoreleasepool and it didn't do anything. Is the AnyCancellable in the code not getting deinitialized? Is something hanging on to it?

Test Playground

Upvotes: 0

Views: 341

Answers (1)

vacawama
vacawama

Reputation: 154583

You are testing this in a Playground. The Swift Playgrounds are notorious for hanging on to objects with an extra reference so that you can interact with them in the Playground. This makes the Playground a poor choice for testing the allocation and freeing of objects.

Try this in a real app and you should find that it works as advertised.


Note: I have found that it will sometimes work out in a Playground if you put all of your code into a function (such as test()) and then call the function. That prevents variables at top level from being defined and hanging on to object references.

Upvotes: 3

Related Questions