Future will crash when it resolves, if handleEvents capture's self and self is de-allocated

Capturing self in handleEvents of Future will crash if when the future resolves, self is de-allocated

The problem is that I want to tie the subcription to the lifecycle of the class that will hold the cancellation token. So that if the class is de-allocated the subscription will receive a cancel event and perform any needed cleanup. This seems not possible, since if I capture a strong self reference in handleEvents the Future will crash if the AClass instance is de-allocated before the future resolves:

class AClass {
    var subscriptions: Set<AnyCancellable> = []
    init() {
        Future<Void, Never> { resolve in
            DispatchQueue.global().asyncAfter(deadline: .now() + 3, execute: {
                print("resolving")
                resolve(.success(()))
                print("resolved")
            })
        }
        .handleEvents(receiveCancel: {
//I need to perform cleanup work here if the self is deallocated
            print("received cancel \(self)")
            self.aFunction()
        })
        .sink { _ in
        }.store(in: &subscriptions)
    }

    deinit {
        print("AClass deinit")
    }

    func aFunction() {}
}

    var aClass: AClass? = AClass()
//If the aClass is dellocated before the future resolves 
//then upon calling resolve(.success(())) it will crash
    aClass = nil
// The above code will output
// > resolving
// > AClass deinit
// > crash with EXC_BAD_INSTRUCTION

The future seems to release the strong captured reference to self at some point during the call to resolve(.success(())), this triggers the deinit of the AClass that triggers the cancellation of the subscription, that triggers internally in future an invalid access to the self instance. Is this the expected behavior? It behaves like it holds and access internally an unowned reference to self?

If I capture a weak reference in the handleEvents

.handleEvents(receiveCancel: { [weak self] in ...

Then it does not crash, and I have the following output

> AClass deinit
> received cancel nil
> resolving
> resolved

But again the problem here is that the handleEvents is called after the self is de-allocated so I can not perform the cleanup I need(e.g. the AClass is a viewController and I present a Alert that I would like to dismiss in the case the viewController is de-allocated).

For easier testing you can run it from Example of Future that will crash when it resolves, if handleEvents capture's self and self is de-allocated ยท GitHub

Terms of Service

Privacy Policy

Cookie Policy