如何在 RxSwift 中使用 BehaviorRelay 替代 Variable?
How to use BehaviorRelay as an alternate to Variable in RxSwift?
从 RxSwift4 开始,Variable
被移动到 Deprecated.swift
,标志着将来可能弃用 Variable
。 Variable
的替代方案是 BehaviorRelay
。在发布这个问题时,由于我在网络上找不到很多使用 BehaviorRelay
的教程,所以我在 SO 中发布了这样一个基本问题。
假设我有一个正在进行的 webService 调用,我收到一大块数据,它是 JSON 数组,在逐个解析 JSON 对象时,我更新变量的值 属性
这是我的变量声明
var myFilter = Variable<[MyFilterModel]>([MyFilterModel(data: "{:}")])
每次我将变量更新为
时都会获取一个新元素
myFilter.value.append(newModel)
由于 Variable 绑定到 CollectionView,collectionVie 将使用新添加的对象立即更新其 UI。
BehaviorRelay 问题
现在我的声明看起来像
var myFilter = BehaviorRelay<[MyFilterModel]>(value: [MyFilterModel(data: "{:}")])
但最大的问题是 myFilter.value
是 readOnly。很明显
myFilter.value.append(newModel)
不是解决方案。我发现我可以使用 accept
而不是。
但是现在当我尝试解析响应中的每个元素并更新 myFilter 的值时
self?.expertsFilter.accept(newModel)
以上语句给出错误引用
Can not convert the value of NewModel to expected arguement type
[NewModel]
显然,它需要一个数组而不是单个元素。
解决方法:
方案一:
所以一个解决方案是将所有响应累积在一个临时数组中,一旦完成触发 self?.expertsFilter.accept(temporary_array)
方案二:
如果我必须在解析每个元素时向订阅者发送 onNext
事件,我需要将 self?.expertsFilter 的值复制到新数组,将新解析的元素添加到它并且 return 新数组。
方案三:
摆脱BehaviorRelay
并使用BehaviorSubject
/PublishSubject
前两个听起来令人沮丧,因为在解析每个元素时可能需要触发 UI 我等不及要解析整个响应。所以很明显solution1用处不大
第二种解决方案更可怕,因为它每次都创建一个新数组(我知道它是临时的并且将被释放)来发送 onNext 事件。
问题:
因为 BehaviorRelay
被提议作为 Variable
的替代品,我陷入困境,我正确使用 accept
吗?有没有更好的方法来解决它?
请帮忙
我会做那样的事 -
let requests = PublishSubject<Observable<ServerResponse>>.create()
let responses: Observable<ServerResponse> = requests.switchLatest()
let parsed: Observable<[ParsedItem]> = responses
.flatMap { Observable.from([=10=]).map { parse([=10=]) }.toArray() }
parsed.bind(to: ui)
// repeated part
let request1: Observable<ServerResponse> = servive.call()
request.onNext(request1)
您是否考虑过简单地从中继上的现有值创建一个新数组,追加,然后调用 accept
?
myFilter.accept(myFilter.value + [newModel])
基于 ,这里有一个方便的扩展:
extension BehaviorRelay where Element: RangeReplaceableCollection {
func acceptAppending(_ element: Element.Element) {
accept(value + [element])
}
}
我写这个扩展是为了用 BehaviorRelay
s 替换 Variable
s。您可以根据此模式添加您需要的任何方法以轻松迁移。
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)
}
}
而不是 Variable.value.funcName
,现在你写 BehaviorRelay.funcName
。
使用 where Element: RangeReplaceableCollection
子句的想法来自
另请注意 index
是 Element.Index
类型,而不是 Int
或其他类型。
AshKan 的回答很好,但我是来这里寻找解决方案中缺少的方法的。
追加:
extension BehaviorRelay where Element: RangeReplaceableCollection {
func append(_ subElement: Element.Element) {
var newValue = value
newValue.append(subElement)
accept(newValue)
}
}
我创建了这个扩展,有两种方法可以方便迁移,以防你有一个数组变量并且你必须使用追加。
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)
}
}
你这样称呼它
var things = BehaviorRelay<[String]>(value: [])
things.append("aa")
let otherThings = ["bb", "cc"]
things.append(contentsOf: otherThings)
things.remove(at: 0)
things.removeAll()
这种扩展怎么样:
extension BehaviorRelay {
var val: Element {
get { value }
set { accept(newValue) }
}
}
然后像使用 Variable
一样使用它,但不是调用 value
,而是调用 val
:
myFilter.val.append(newModel)
On 变量曾经有:
let variable = Variable("Hello RxSwift")
variable.value = "Change text"
print(variable.value) // "Change text"
在 BehaviorRelay 上你必须使用:
let relay = BehaviorRelay(value: "Hello RxSwift")
relay.accept("Change text")
print(relay.value) // "Change text"
从 RxSwift4 开始,Variable
被移动到 Deprecated.swift
,标志着将来可能弃用 Variable
。 Variable
的替代方案是 BehaviorRelay
。在发布这个问题时,由于我在网络上找不到很多使用 BehaviorRelay
的教程,所以我在 SO 中发布了这样一个基本问题。
假设我有一个正在进行的 webService 调用,我收到一大块数据,它是 JSON 数组,在逐个解析 JSON 对象时,我更新变量的值 属性
这是我的变量声明
var myFilter = Variable<[MyFilterModel]>([MyFilterModel(data: "{:}")])
每次我将变量更新为
时都会获取一个新元素myFilter.value.append(newModel)
由于 Variable 绑定到 CollectionView,collectionVie 将使用新添加的对象立即更新其 UI。
BehaviorRelay 问题
现在我的声明看起来像
var myFilter = BehaviorRelay<[MyFilterModel]>(value: [MyFilterModel(data: "{:}")])
但最大的问题是 myFilter.value
是 readOnly。很明显
myFilter.value.append(newModel)
不是解决方案。我发现我可以使用 accept
而不是。
但是现在当我尝试解析响应中的每个元素并更新 myFilter 的值时
self?.expertsFilter.accept(newModel)
以上语句给出错误引用
Can not convert the value of NewModel to expected arguement type [NewModel]
显然,它需要一个数组而不是单个元素。
解决方法:
方案一:
所以一个解决方案是将所有响应累积在一个临时数组中,一旦完成触发 self?.expertsFilter.accept(temporary_array)
方案二:
如果我必须在解析每个元素时向订阅者发送 onNext
事件,我需要将 self?.expertsFilter 的值复制到新数组,将新解析的元素添加到它并且 return 新数组。
方案三:
摆脱BehaviorRelay
并使用BehaviorSubject
/PublishSubject
前两个听起来令人沮丧,因为在解析每个元素时可能需要触发 UI 我等不及要解析整个响应。所以很明显solution1用处不大
第二种解决方案更可怕,因为它每次都创建一个新数组(我知道它是临时的并且将被释放)来发送 onNext 事件。
问题:
因为 BehaviorRelay
被提议作为 Variable
的替代品,我陷入困境,我正确使用 accept
吗?有没有更好的方法来解决它?
请帮忙
我会做那样的事 -
let requests = PublishSubject<Observable<ServerResponse>>.create()
let responses: Observable<ServerResponse> = requests.switchLatest()
let parsed: Observable<[ParsedItem]> = responses
.flatMap { Observable.from([=10=]).map { parse([=10=]) }.toArray() }
parsed.bind(to: ui)
// repeated part
let request1: Observable<ServerResponse> = servive.call()
request.onNext(request1)
您是否考虑过简单地从中继上的现有值创建一个新数组,追加,然后调用 accept
?
myFilter.accept(myFilter.value + [newModel])
基于
extension BehaviorRelay where Element: RangeReplaceableCollection {
func acceptAppending(_ element: Element.Element) {
accept(value + [element])
}
}
我写这个扩展是为了用 BehaviorRelay
s 替换 Variable
s。您可以根据此模式添加您需要的任何方法以轻松迁移。
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)
}
}
而不是 Variable.value.funcName
,现在你写 BehaviorRelay.funcName
。
使用 where Element: RangeReplaceableCollection
子句的想法来自
另请注意 index
是 Element.Index
类型,而不是 Int
或其他类型。
AshKan 的回答很好,但我是来这里寻找解决方案中缺少的方法的。 追加:
extension BehaviorRelay where Element: RangeReplaceableCollection {
func append(_ subElement: Element.Element) {
var newValue = value
newValue.append(subElement)
accept(newValue)
}
}
我创建了这个扩展,有两种方法可以方便迁移,以防你有一个数组变量并且你必须使用追加。
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)
}
}
你这样称呼它
var things = BehaviorRelay<[String]>(value: [])
things.append("aa")
let otherThings = ["bb", "cc"]
things.append(contentsOf: otherThings)
things.remove(at: 0)
things.removeAll()
这种扩展怎么样:
extension BehaviorRelay {
var val: Element {
get { value }
set { accept(newValue) }
}
}
然后像使用 Variable
一样使用它,但不是调用 value
,而是调用 val
:
myFilter.val.append(newModel)
On 变量曾经有:
let variable = Variable("Hello RxSwift")
variable.value = "Change text"
print(variable.value) // "Change text"
在 BehaviorRelay 上你必须使用:
let relay = BehaviorRelay(value: "Hello RxSwift")
relay.accept("Change text")
print(relay.value) // "Change text"