Since observables are typically IDisposable
how does that change, if at all, the need to use weak references in event handlers, or any other event based memory leak/GC locked referencing?
While my primary concern/need is for WPF I'm looking for the broader example and trying to understand where I may need weak references.
F#'s Observable.add
doesn't provide a way to unhook the event, so I'm thinking it's less likely to be a source of leaks.
Sample code:
type Notifier() =
let propChanged = new Event<_,_>()
member __.Foo() = ()
interface INotifyPropertyChanged with
[<CLIEvent>]
member __.PropertyChanged = propChanged.Publish
abstract member RaisePropertyChanged : string -> unit
default x.RaisePropertyChanged(propertyName : string) = propChanged.Trigger(x, PropertyChangedEventArgs(propertyName))
Notifier() :?> INotifyPropertyChanged
|> Observable.add(fun _ -> printfn "I'm hooked on you")
It's actually the opposite.
Observable.add
, by the docs, permanently subscribes to the event, and forces a "leak". It's effectively doing an event handler addition that has no way to unsubscribe.In general, with
Observable
(in F# and C#), you should favor using.subscribe
, and disposing of the subscription handle when you're done.As @rmunn mentioned, Gjallarhorn can serve as an alternative to using observables in some scenarios (and integrates nicely with them as needed). While writing it, one of my main goals was to make it so that subscriptions don't leak - all of the subscriptions use a hybrid push/pull model based on weak references, which prevents many of the problems with leaking in event and observable based code.
To demonstrate, I've thrown together a variation on your code, using both observables and Gjallarhorn's signals. If you run this in a release build, outside of the debugger, you'll see the difference:
Note that both do something similar - they create a "source", then, in a separate scope, subscribe to it and throw away the disposable subscription handle (
Observable.add
is nothing butsubscribe |> ignore
- see code for details.). When running in a release build outside of the debugger (the debugger prevents cleanup from happening), you see:In the observable case, the call to
.add
permanently holds a reference to the notifier, preventing it from being garbage collected. With signals, the signal subscription will GC, and "unhook" automatically, preventing the calls from Hit from ever being displayed.