Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about Signal leak / memory managment #92

Closed
emuye opened this issue Nov 10, 2016 · 3 comments
Closed

Question about Signal leak / memory managment #92

emuye opened this issue Nov 10, 2016 · 3 comments

Comments

@emuye
Copy link

emuye commented Nov 10, 2016

I'm seeing some leaks with ReactiveSwift. I'm using the alpha so wondering if this is known, or if I'm missing something.

Using ReactiveCocoa (5.0.0-alpha.3)
Using ReactiveSwift (1.0.0-alpha.3)
Using Result (3.0.0)

Why does this code leak a Signal?

screen shot 2016-11-10 at 11 45 50 am

 var disposable = CompositeDisposable()  
  deinit {
       disposable.dispose()
   }

   @IBAction func load(_ sender: Any) {
       disposable += SignalProducer<Void, NSError> { sink, disposable in
           disposable.add {
               print("dispose")
           }
           
           sink.send(value: ())
           sink.sendCompleted()
       }.startWithCompleted {
           print("completed")
       }
   }

Also a similar example with a Disposable

       var disposable: Disposable?
       deinit {
            disposable?.dispose()
        }

       disposable = SignalProducer<Void, NSError> { sink, disposable in
            sink.send(value: ())
            sink.sendCompleted()
        }.startWithCompleted {
            print("completed")
        }

@andersio
Copy link
Member

andersio commented Nov 10, 2016

If you obtain a reference to the produced signal, it does turn nil after completion.

This is caused by a caveat of Swift's ref-counting implementation: while the object is deinitialized after its last strong reference is ceased, the object would not be freed until all its weak references have been touched.

If you do not retain the cancel disposable, which indirectly holds a weak reference to the signal, Instruments would no longer catch this leak.

https://github.com/apple/swift/blob/master/docs/weak.rst#implementation
(Despite not being updated post-1.0, Swift's ref counting still works this way)

However, it does have a very important downside: since the system cannot clear all the references, it is impossible to actually deallocate an object that is still weakly-referenced (although it can be finalized). Instead, the system must wait for all the weak references to at least be accessed. We call this "husk leaking".

@emuye
Copy link
Author

emuye commented Nov 11, 2016

@andersio Thanks for responding so quicky. Can you show me the code for seeing the reference go to nil after completion?

@andersio
Copy link
Member

andersio commented Nov 11, 2016

Instruments caught a leak. But the signal is freed after accessing the weak signal property (which gives you a nil), or when self deinitializes.

    weak var signal: Signal<(), NoError>?
    @IBAction func buttonTouchedUpInside(_ sender: Any) {
       SignalProducer<(), NoError>.empty.startWithSignal { signal, _ 
           self.signal = signal
       }
    }

No leak was caught.

    @IBAction func buttonTouchedUpInside(_ sender: Any) {
       SignalProducer<(), NoError>.empty.startWithCompleted {}
    }

Instruments caught a leak, but the signal should be freed after the disposable is thrown away.

    var disposable: Disposable?
    @IBAction func buttonTouchedUpInside(_ sender: Any) {
       disposable = SignalProducer<(), NoError>.empty.startWithCompleted {}
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants