2012-01-18 8 views
6

He analizado los ejemplos de ReactiveUi en los blogs y me pregunto si ReactiveUI tiene alguna clase de instalación de gestión de suscripciones o si los ejemplos ignoran el hecho de que pueden filtrar las suscripciones.¿Suscripciones de fugas de ReactiveUI?

Cada vez que llamo a un método en ReactiveUi que da como resultado un IDisposable, ¿necesito conservar esa referencia y rastrearla yo mismo? También significa que mis ViewModels deben ser desechables, esto parece difícil ya que no sabemos realmente cuándo desaparecen las "Vistas" conectadas (es decir, si mi ViewModel refleja elementos en una cuadrícula de datos) en WPF, por lo que parece no ser apropiado lugar para llamar a disponer

Respuesta

10

Solo necesita mantener referencias al IDisposable devuelto por las suscripciones si necesita anular la suscripción al inicio de un observable. Observables llamará naturalmente al Dispose cuando finalicen con los mensajes OnCompleted o OnError.

Sin embargo, necesita mantener referencias cuando tiene una suscripción infinita observable (es decir, FromEventPattern), pero esto es exactamente lo mismo que necesitar eliminar los controladores de eventos antes de cerrar un formulario/vista.

+0

Normalmente no tiene que quitar los controladores de eventos al cerrar el formulario si la duración del editor es igual o menor que el tiempo de vida del suscriptor.¿Se aplica el mismo principio a ReactiveCommand, por ejemplo? – KolA

+1

@KolA - Yo sugeriría que elimine explícitamente todas las suscripciones de Rx si sabe que algunas podrían estar ejecutándose solo para estar seguras. Es bastante fácil tener un 'CompositeDisposable' a nivel de formulario que rastrea todas las suscripciones. Significa solo un '.Dispose()' al salir. Al igual que los eventos regulares, puede escaparse sin hacerlo, pero no siempre es así. – Enigmativity

13

También debe recordar que los Idisposables devueltos por Rx y ReactiveUI no están asociados a la memoria no administrada: se trata simplemente de objetos .NET simples, aún ref'ed por el recolector de elementos no utilizados.

La mayoría de las Suscripciones que realice en los constructores de sus ReactiveObjects estarán vinculadas a la duración del objeto host, por lo que cuando se salga del alcance y esté sujeto a GC, también lo harán todas las suscripciones, el CLR detectará la referencia circular y simplemente nuke todo.

Como menciona Enigmativity, el único inconveniente es cuando se utiliza FromEventPattern para vincular el tiempo de vida de una Suscripción (y tal vez, un ViewModel) con la duración de un objeto WPF. Sin embargo, yo diría que si usas FromEventPattern a menudo en ReactiveUI, definitivamente estás haciendo lo incorrecto.

RxUI tiene que ver con ViewModels y ViewModels son todos unos comandos y propiedades (y cableado hasta qué propiedades están relacionadas entre sí), por lo que puede probar el comportamiento de la experiencia del usuario por separado de sus elementos visuales .

+0

Pero, ¿no es el uso de las propiedades y los "eventos" ObservedChanged solo usando EventPattern en los eventos PropertyChanged y PropertyChanging? ¿Y no es el uso de Comandos enganchados en el evento Ejecutado? Supongo que esto es lo que me preocupa. – Damian

+0

No, internamente ReactiveObject usa un Subject, no estás usando FromEventPattern cuando usas ObservableForProperty o WhenAny –

+0

Okay. Gracias por la aclaración. Estoy marcando la respuesta de Enigmativity como la respuesta ya que él estaba en lo cierto primero, pero tu aclaración fue crucial para mi nivel de conversión. – Damian

2

Solo para estar seguro, voy a utilizar este patrón a partir de ahora. Probablemente voy a modificar para trabajar a través de métodos de extensión, pero el principio es el sonido. Para asegurarse de que no se escapan suscripciones que desea dejarlos

class Mesh2D{ 

    public Mesh2D() 
    { 
     DisposeOnUnload(CreateBindings()); 
    } 

    // Register all disposables for disposal on 
    // UIElement.Unload event. This should be 
    // moved to an extension method. 
    void DisposeOnUnload(IEnumerable<IDisposable> disposables) 
    { 
     var d = new CompositeDisposable(disposables); 
     var d2 = this.UnloadedObserver() 
      .Subscribe(e => d.Dispose()); 
     var d3 = this.Dispatcher.ShutdownStartedObserver() 
      .Subscribe(e => d.Dispose()); 
     d.Add(d2); 
     d.Add(d3); 
    } 

    // Where your bindings are simply yielded and 
    // they are removed on Unload magically 
    IEnumerable<IDisposable> CreateBindings() 
    { 
     // When the size changes we need to update all the transforms on 
     // the markers to reposition them. 
     yield return this.SizeChangedObserver() 
      .Throttle(TimeSpan.FromMilliseconds(20), RxApp.DeferredScheduler) 
      .Subscribe(eventArgs => this.ResetImageSource()); 

     // If the points change or the viewport changes 
     yield return this.WhenAny(t => t.Mesh, t => t.Viewport, (x, t) => x.Value) 
      .Throttle(TimeSpan.FromMilliseconds(20), RxApp.DeferredScheduler) 
      .Subscribe(t => this.UpdateMeshChanged()); 
    } 
} 

Nota estoy envolviendo RX FromEventPattern con métodos de extensión generados automáticamente por lo que obtener SizeChangedObserver() y UnloadedObserver() de forma gratuita en lugar de el formato difícil de recordar de FromEventPattern.

El código de envoltura se genera como tal

public static IObservable<EventPattern<RoutedEventArgs>> UnloadedObserver(this FrameworkElement This){ 
    return Observable.FromEventPattern<RoutedEventHandler, RoutedEventArgs>(h => This.Unloaded += h, h => This.Unloaded -= h); 
} 

el patrón anterior, probablemente, se podría utilizar para desenlazar IDisposable Ver modelos también.