首页 > 解决方案 > RxSwift 变量到另一个变量的链式传播

问题描述

这是我们用例的一个例子:

我们有一个selectedIndex和 的列表items

class FoosViewModel {
    let selectedIndex = Variable<Int>(0)
    let items: [Foo] = ... // assume that this is initialized properly
}

现实中,我们经常关心的是选中了哪个item,而不是被选中item的index。所以我们会有这样的代码:

selectedIndex.asObservable().subscribe(onNext: { [weak self] index in
    guard let self = self else { return }
    let selectedItem = items[index]

    // Do sth with `selectedItem` here
}

请注意, 的值selectedItem始终由 驱动selectedIndex。因此,我们将代码更改为以下内容:

class FoosViewModel {
    let selectedIndex = Variable<Int>(0)
    let selectedItem = Variable<Int>(items[0])
    let items: [Foo] = ... // assume that this is initialized properly

    init() {
       selectedIndex.asObservable().subscribe(onNext: { [weak self] index in
           guard let self = self else { return }
           self.selectedItem = items[index]
        }
    }
}

这似乎是一个足够常见的用例。我们在 Rx 中是否有一个可以将 a 映射Variable到另一个的现有运算符?有没有这样的:

class FoosViewModel {
    let selectedIndex = Variable<Int>(0)
    let selectedItem = selectedIndex.map{ items[$0] }
    let items: [Foo] = ... // assume that this is initialized properly
}

标签: rx-swift

解决方案


您所做的是创建了两个相互依赖的状态。最好只有一个事实来源和一个衍生品,这意味着应该以不同于另一个的方式实施。假设这selectedIndex是事实的来源,那么我希望看到:

class FoosViewModel {
    let selectedIndex = Variable<Int>(0)
    let selectedItem: Observable<Foo?>
    let items: [Foo]

    init(items: [Foo]) {
        selectedItem = selectedIndex.asObservable().map { index in 
            index < items.count ? items[$0] : nil 
        }
        self.items = items
    }
}

与您的尝试不同,此类用户不会尝试分配新值selectedItem(事实上,如果您尝试,代码甚至不会编译。)作为一个附带好处,没有必要这样做“弱自我舞蹈”要么因为地图根本没有提到self。所有这些都有效,因为你做items了一个 let 而不是 var (对你有好处!)

如果您希望能够添加/删除项目,那么事情会变得更加复杂......

class MutableFooViewModel {
    let selectedIndex = Variable<Int>(0)
    let selectedItem: Observable<Foo?>
    let items = Variable<[Foo]>([])

    init(items: [Foo]) {
        items.value = items
        let _items = self.items // this is done to avoid reference to `self` in the below.
        selectedItem = Observable.combineLatest(
          _items.asObservable(), 
          selectedIndex.asObservable()
        ) { items, index in 
            index < items.count ? items[index] : nil 
        }
    }
}

这里的想法是,在制作依赖于其他可观察对象的可观察对象时,主题(变量是一种主题)不应该是您首先想到的。在这方面,它们仅适用于创建初始可观察对象。(RxCocoa 充满了它们。)


哦,顺便说一句,Variable已被弃用。


推荐阅读