Rxswift:避免嵌套订阅
Rxswift: Avoid nested subscirption
我有一个要订阅的 Observable 对象数组,每个对象都必须与 UI 组件绑定,并且在 UI 组件内我必须在子视图上添加点击手势,这需要另一个订阅。我是 RxSwift 的新手,阅读了一些类似的问题和文章,但要么我无法理解,要么内容不太相关。
items.subscribe(onNext: { [weak self] items in
for item in items {
let view = aView()
view.label.text = item.name
view.image.rx.touchUpInside.subscribe(onNext: { [viewModel] _ in
doSomething()
}).disposed(by: disposeBag)
self?.stackView.addArrangedSubview(view)
}
}).disposed(by: disposeBag)
我读过 flatMap
here and a similar question here 但无法理解。请稍微解释一下您的回答,以便我可以学习解决类似的问题。
您走在正确的轨道上,flatMap
就是您想要的。可能让您感到困惑的是您想要订阅很多 Observable(在您的 for 循环中)而不是一个。所以让我们先分解一下。
对于您要创建视图的数组中的每个项目,为其命名,将其添加到您的 stackView,然后收听 touchUpInside
可观察对象。因此,为此创建一个函数(这实际上是获取 for 循环的主体并将其变成一个自由函数。):
func addView(to stackView: UIStackView, for item: Item) -> Observable<Void> {
let view = aView()
view.label.text = item.name
stackView.addArrangedSubview(view)
return view.image.rx.touchUpInside
}
注意上面是一个自由函数。不要在你的 class 中使用它;它不需要自我。
重构后,您的原始代码如下所示:
items.subscribe(onNext: { [weak self] items in
for item in items {
addView(to: self!.stackView, for: item)
.subscribe(onNext: { [viewModel = self!.viewModel] _ in
doSomething()
})
.disposed(by: self!.disposeBag)
}
})
.disposed(by: disposeBag)
接下来我们看到您正在对数组中的每个项目执行相同的操作,并且它们的所有订阅都会导致调用相同的 doSomething()
...所以让我们映射数组并合并可观察...
func addViewToStack(from item: Item) -> Observable<Void> {
addView(to: stackView, for: item)
}
items.subscribe(onNext: { [weak self] items in
Observable.merge(items.map { addViewToStack(from: [=12=]) })
.subscribe(onNext: { [viewModel = self!.viewModel] _ in
doSomething()
})
.disposed(by: self!.disposeBag)
})
.disposed(by: disposeBag)
注意,将 addViewToStack(from:)
函数嵌入到您当前的函数中;不要让它成为一种方法。这样它将捕获 stackView
而不会捕获 self.
从这里...我们看到 Observable.merge(items.map(addViewToStack(from:)))
需要一个项目数组和 returns 一个单独的 Observable,这正是 flatMap
的签名所需要的...
items
.flatMap { Observable.merge([=13=].map { addViewToStack(from: [=13=]) }) }
.subscribe(onNext: { [viewModel] _ in
doSomething()
})
.disposed(by: disposeBag)
现在我们没有了inner-subscribe,我们也不再需要捕获self了。
总的来说,结果看起来像这样:
class Example {
let stackView = UIStackView()
let viewModel = ViewModel()
let disposeBag = DisposeBag()
func example(items: Observable<[Item]>) {
func addViewToStack(from item: Item) -> Observable<Void> {
addView(to: stackView, for: item)
}
items
.flatMap { Observable.merge([=14=].map { addViewToStack(from: [=14=]) }) }
.subscribe(onNext: { [viewModel] _ in
doSomething()
})
.disposed(by: disposeBag)
}
}
func addView(to stackView: UIStackView, for item: Item) -> Observable<Void> {
let view = aView()
view.label.text = item.name
stackView.addArrangedSubview(view)
return view.image.rx.touchUpInside
}
我有一个要订阅的 Observable 对象数组,每个对象都必须与 UI 组件绑定,并且在 UI 组件内我必须在子视图上添加点击手势,这需要另一个订阅。我是 RxSwift 的新手,阅读了一些类似的问题和文章,但要么我无法理解,要么内容不太相关。
items.subscribe(onNext: { [weak self] items in
for item in items {
let view = aView()
view.label.text = item.name
view.image.rx.touchUpInside.subscribe(onNext: { [viewModel] _ in
doSomething()
}).disposed(by: disposeBag)
self?.stackView.addArrangedSubview(view)
}
}).disposed(by: disposeBag)
我读过 flatMap
here and a similar question here 但无法理解。请稍微解释一下您的回答,以便我可以学习解决类似的问题。
您走在正确的轨道上,flatMap
就是您想要的。可能让您感到困惑的是您想要订阅很多 Observable(在您的 for 循环中)而不是一个。所以让我们先分解一下。
对于您要创建视图的数组中的每个项目,为其命名,将其添加到您的 stackView,然后收听 touchUpInside
可观察对象。因此,为此创建一个函数(这实际上是获取 for 循环的主体并将其变成一个自由函数。):
func addView(to stackView: UIStackView, for item: Item) -> Observable<Void> {
let view = aView()
view.label.text = item.name
stackView.addArrangedSubview(view)
return view.image.rx.touchUpInside
}
注意上面是一个自由函数。不要在你的 class 中使用它;它不需要自我。
重构后,您的原始代码如下所示:
items.subscribe(onNext: { [weak self] items in
for item in items {
addView(to: self!.stackView, for: item)
.subscribe(onNext: { [viewModel = self!.viewModel] _ in
doSomething()
})
.disposed(by: self!.disposeBag)
}
})
.disposed(by: disposeBag)
接下来我们看到您正在对数组中的每个项目执行相同的操作,并且它们的所有订阅都会导致调用相同的 doSomething()
...所以让我们映射数组并合并可观察...
func addViewToStack(from item: Item) -> Observable<Void> {
addView(to: stackView, for: item)
}
items.subscribe(onNext: { [weak self] items in
Observable.merge(items.map { addViewToStack(from: [=12=]) })
.subscribe(onNext: { [viewModel = self!.viewModel] _ in
doSomething()
})
.disposed(by: self!.disposeBag)
})
.disposed(by: disposeBag)
注意,将 addViewToStack(from:)
函数嵌入到您当前的函数中;不要让它成为一种方法。这样它将捕获 stackView
而不会捕获 self.
从这里...我们看到 Observable.merge(items.map(addViewToStack(from:)))
需要一个项目数组和 returns 一个单独的 Observable,这正是 flatMap
的签名所需要的...
items
.flatMap { Observable.merge([=13=].map { addViewToStack(from: [=13=]) }) }
.subscribe(onNext: { [viewModel] _ in
doSomething()
})
.disposed(by: disposeBag)
现在我们没有了inner-subscribe,我们也不再需要捕获self了。
总的来说,结果看起来像这样:
class Example {
let stackView = UIStackView()
let viewModel = ViewModel()
let disposeBag = DisposeBag()
func example(items: Observable<[Item]>) {
func addViewToStack(from item: Item) -> Observable<Void> {
addView(to: stackView, for: item)
}
items
.flatMap { Observable.merge([=14=].map { addViewToStack(from: [=14=]) }) }
.subscribe(onNext: { [viewModel] _ in
doSomething()
})
.disposed(by: disposeBag)
}
}
func addView(to stackView: UIStackView, for item: Item) -> Observable<Void> {
let view = aView()
view.label.text = item.name
stackView.addArrangedSubview(view)
return view.image.rx.touchUpInside
}