我正在学习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
合并,然后取消订阅。
但问题的另一部分是第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
,因为它仍然保留对点击事件的引用(因为{{ 1}})。
这是内存泄漏,我是对的吗?
那么,问题是如何正确处理可以动态添加/删除的对象的Observable?
答案 0 :(得分:0)
在深入研究问题的同时,我找到了3种可能的解决方案:
对于该特定情况,我可以使用控件的Disposed
事件:
var incsFromWidgets = addCounterStream
.SelectMany(x => Observable.FromEventPattern(x, "Increment")
.Select(_ => 1)
.TakeUntil(Observable.FromEventPattern(x, "Disposed").Take(1)))
.Publish();
实际上它是第一种方法的概括:我可以创建具有发布语义的通用包装类,如:
class Wrapper<T> : IDisposable {
public T Value { get; set; }
public event EventHandler Disposed;
}
手动“吊装”溪流:
// 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);