2016-10-08 102 views
1

我正在學習Rx-stuff,並且製作了一個測試應用程序,該應用程序具有共享計數器狀態並動態創建計數器窗口小部件。比如你可以按添加/刪除按鈕來產生/銷燬一個新的計數器。它工作正常,但我不知道如何正確處置刪除的計數器小部件。取消訂閱動態觀察對象

Here是我的源代碼。

問題的一部分是在行77

// update each counter's label 
addCounterStream.Subscribe(x => { 
    counterState.Subscribe(q => x.Counter = q.ToString()); // How can I unsubscribe later? 
}); 

和可能的解決方案,我能想到的,是建立一個Subject<IDisposable>這將收集這些訂閱,然後用removeCounterStream結合此流,做unsibscribe。

但問題的另一部分是在線路40,41:

var incFromWidget = addCounterStream.SelectMany(x => Observable.FromEventPattern(x, "Increment").Select(_ => 1)); 
var decFromWidget = addCounterStream.SelectMany(x => Observable.FromEventPattern(x, "Decrement").Select(_ => -1)); 

它融合了所有動態創建控件的單擊事件。如果我想刪除一些小部件,會發生什麼?它將從表單中消失,因此無法產生新的點擊事件,但不能生成GC',因爲它仍然引用點擊事件(因爲FromEventPattern)。

這是一個內存泄漏,我說得對嗎?

所以,問題是如何正確處理可以動態添加/刪除對象的Observable?

+0

難道你不能簡單地處置它嗎? https://msdn.microsoft.com/zh-cn/library/hh242977(v=vs.103).aspx – pix

+0

可能重複? http://stackoverflow.com/questions/3449834/rx-unsubscribeing-from-events?rq=1 – pix

+1

@pix我無法處理它,因爲它會停止來自所有小部件的事件,而且我只需要處理關於小部件被刪除(當點擊刪除計數器按鈕時)。我可以通過特定的窗口小部件(在第40,41行)過濾(或者可能是GroupBy)事件,但是如果我從不訂閱,我該如何處置它們?因爲我只訂閱'counterState'(由這兩個組成)。然後再次,GroupBy是一個緩衝操作,這將導致內存泄漏再次。 – saintcrawler

回答

0

雖然挖掘更深的問題,我發現了3級可能的解決方案:

  1. 該特定情況下,我可以使用Disposed事件的控制是這樣的:

    var incsFromWidgets = addCounterStream 
         .SelectMany(x => Observable.FromEventPattern(x, "Increment") 
          .Select(_ => 1) 
          .TakeUntil(Observable.FromEventPattern(x, "Disposed").Take(1))) 
         .Publish(); 
    
  2. 實際上它的泛化第一種方法:我可以製作帶釋放語義的通用包裝類,如:

    class Wrapper<T> : IDisposable { 
        public T Value { get; set; } 
        public event EventHandler Disposed;   
    } 
    
  3. 手動流的「吊裝」:

    // declare all streams 
    IObservable<int> aStream = null; 
    IObservable<bool> bStream = null; 
    IObservable<MyWidget> cStream = null; 
    
    // then compose it 
    aStream = Observable.From(...).TakeUntil(bStream.Where(...)); 
    bStream = Observable.From(...).CombineLatest(aStream, ...); // cyclic dependency 
    

雖然第三方法可能看起來可怕,它提供了巨大的可能性,以流以不同的方式結合起來,只是需要小心,不要創建無限遞歸。

編輯: 嗯,其實第三個方法是不那麼簡單,它會拋出NullReferenceException異常,即使流定義如下。正確的實施將會像下面這樣:

 IConnectableObservable<int> a = null; 
     IConnectableObservable<char> b = null; 
     IConnectableObservable<bool> c = null;    

     a = Observable.Defer(() => b.Select(x => (int)x)).Publish(); 
     b = Observable.Defer(() => Observable.Generate('A', x => x < 255, x => (char)(x + 1), x => x).TakeUntil(c)).Publish(); 
     c = Observable.Defer(() => a.SkipWhile(x => x < 70).Select(_ => true)).Publish(); 

     a.Subscribe(x => Console.WriteLine($"from a: {x}")); 
     b.Subscribe(x => Console.WriteLine($"from b: {x}")); 

     c.Connect(); 
     a.Connect(); 
     b.Connect(); 

     Console.ReadKey(true);