如何使用BehaviorRelay作为RxSwift中Variable的替代?

San*_*ari 48 rx-swift

从RxSwift4开始,Variable移动到Deprecated.swift标记Variable未来可能的弃用.建议的替代方案VariableBehaviorRelay.在发布这个问题时,由于我在网上找不到太多的教程,BehaviorRelay我在SO中发布了这样一个基本问题.

假设我正在进行webService调用,并且我收到一块JSONArray数据,在逐个解析JSON对象时我更新了我的Variable的value属性

这是我的变量声明

var myFilter = Variable<[MyFilterModel]>([MyFilterModel(data: "{:}")])
Run Code Online (Sandbox Code Playgroud)

每次我将变量更新为获取新元素时

myFilter.value.append(newModel)
Run Code Online (Sandbox Code Playgroud)

当Variable绑定到CollectionView时,collectionVie将立即使用新添加的对象更新其UI.

使用BehaviorRelay的问题

现在我的声明看起来像

var myFilter = BehaviorRelay<[MyFilterModel]>(value: [MyFilterModel(data: "{:}")])
Run Code Online (Sandbox Code Playgroud)

但最大的问题myFilter.valuereadOnly.很明显

myFilter.value.append(newModel) 
Run Code Online (Sandbox Code Playgroud)

不是解决方案.我发现我可以使用accept.

但是现在当我尝试解析响应中的每个元素并更新myFilter的值时

self?.expertsFilter.accept(newModel)
Run Code Online (Sandbox Code Playgroud)

上面的陈述给出了错误引用

无法将NewModel的值转换为预期的争论类型[NewModel]

显然,它期待一个数组而不是一个单独的元素.

解决方法:

解决方案1:

因此,一个解决方案是在临时数组中累积所有响应并且一旦完成触发 self?.expertsFilter.accept(temporary_array)

解决方案2:

如果我必须onNext在解析每个元素时向订阅者发送事件,我需要将self?.expertsFilter的值复制到new Array,将新解析的元素添加到它并返回新数组.

解决方案3:

摆脱BehaviorRelay并使用BehaviorSubject/PublishSubject

前两个声音令人沮丧,因为可能需要在解析每个元素时触发UI我不能等到解析整个响应.因此,解决方案1显然没有多大用处.

第二种解决方案更加可怕,因为它每次发送onNext事件时都会创建一个新数组(我知道它是临时的并且将被释放).

题:

因为BehaviorRelay被提议作为Variable进入两难的替代方案,我accept正确使用?有没有更好的方法来解决它?

请帮忙

dal*_*ook 19

您是否考虑过从继电器上的现有值创建一个新数组,追加,然后调用accept

myFilter.accept(myFilter.value + [newModel])
Run Code Online (Sandbox Code Playgroud)

  • 当创建一个新数组并发出一个新数组而不是仅仅追加到现有数组的末尾时,性能会受到什么影响? (2认同)

小智 10

基于道尔顿的答案,这是一个方便的扩展:

extension BehaviorRelay where Element: RangeReplaceableCollection {
    func acceptAppending(_ element: Element.Element) {
        accept(value + [element])
    }
}
Run Code Online (Sandbox Code Playgroud)


Ash*_*lak 9

我写了这个扩展名,用Variables 代替BehaviorRelays。您可以根据此模式添加所需的任何方法来轻松迁移。

public extension BehaviorRelay where Element: RangeReplaceableCollection {

    public func insert(_ subElement: Element.Element, at index: Element.Index) {
        var newValue = value
        newValue.insert(subElement, at: index)
        accept(newValue)
    }

    public func insert(contentsOf newSubelements: Element, at index: Element.Index) {
        var newValue = value
        newValue.insert(contentsOf: newSubelements, at: index)
        accept(newValue)
    }

    public func remove(at index: Element.Index) {
        var newValue = value
        newValue.remove(at: index)
        accept(newValue)
    }
}
Run Code Online (Sandbox Code Playgroud)

相反Variable.value.funcName,现在您写BehaviorRelay.funcName

使用where Element: RangeReplaceableCollection子句的想法来自retendo的答案

还要注意,indexis是类型Element.Index,不是Int或其他任何类型。


saz*_*azz 5

我创建了这些扩展,使用两种方法来促进迁移,以防您有数组变量并且必须使用附加。

    extension BehaviorRelay where Element: RangeReplaceableCollection {

        func append(_ subElement: Element.Element) {
            var newValue = value
            newValue.append(subElement)
            accept(newValue)
        }

        func append(contentsOf: [Element.Element]) {
            var newValue = value
            newValue.append(contentsOf: contentsOf)
            accept(newValue)
        }

        public func remove(at index: Element.Index) {
            var newValue = value
            newValue.remove(at: index)
            accept(newValue)
        }

        public func removeAll() {
            var newValue = value
            newValue.removeAll()
            accept(newValue)
        }

    }
Run Code Online (Sandbox Code Playgroud)

你这样称呼它

    var things = BehaviorRelay<[String]>(value: [])
    things.append("aa")
    let otherThings = ["bb", "cc"]
    things.append(contentsOf: otherThings) 
    things.remove(at: 0)
    things.removeAll()
Run Code Online (Sandbox Code Playgroud)