简体   繁体   English

如何在 Eureka SelectableSection 上进行适当的响应式扩展

[英]How to make a proper reactive extension on Eureka SelectableSection

This is my first question to the StackOverflow community so excuse me if I'm doing something wrong.这是我向 StackOverflow 社区提出的第一个问题,如果我做错了什么,请见谅。

1. What I'm trying to achieve 1. 我想要达到的目标
Basically, I want to make a custom reactive wrapper around Eureka's SelectableSection class in order to observe the value of the selected row when it is changed.基本上,我想在 Eureka 的SelectableSection class 周围制作一个自定义反应式包装器,以便在更改时观察所选行的值。 I'm thinking to get this data from the onSelectSelectableRow closure which is called every time a row is selected.我正在考虑从每次选择一行时调用的onSelectSelectableRow闭包中获取这些数据。

2. What I've tried to do for that 2. 我为此做了什么
Actually, I've got this working but it's not a generic use of the custom wrapper, here is the example that works but only when I specify the row and its value type, for example ListCheckRow<Int> .实际上,我已经完成了这项工作,但它不是自定义包装器的通用用法,这是一个有效的示例,但仅在我指定行及其值类型时才有效,例如ListCheckRow<Int>

extension SelectableSection: ReactiveCompatible {}
extension Reactive where Base : SelectableSection<ListCheckRow<Int>> {
  var selectedValue: Observable<Base.SelectableRow.Cell.Value?> {
    return Observable.create { observer in
      self.base.onSelectSelectableRow = {cell, row in
        observer.onNext(row.value)
      }
      return Disposables.create {
        observer.onCompleted()
      }
    }
  }
}

This works fine and as I expected but when it comes to something more generic like the next code example, I get an error saying that: "Cannot assign to property: 'base' is a 'let' constant"这工作正常,正如我预期的那样,但是当涉及到像下一个代码示例这样更通用的东西时,我收到一条错误消息: “无法分配给属性:'base' 是 'let' 常量”

extension SelectableSection: ReactiveCompatible {}
extension Reactive where Base : SelectableSectionType {
  var selectedValue: Observable<Base.SelectableRow.Cell.Value?> {
    return Observable.create { observer in
      self.base.onSelectSelectableRow = {cell, row in // Error: Cannot assign to property: 'base' is a 'let' constant
        observer.onNext(row.value)
      }
      return Disposables.create {
        observer.onCompleted()
      }
    }
  }
}

Any help will be much appreciated, thanks.任何帮助将不胜感激,谢谢。

The fundamental problem here is that SelectableSectionType is a protocol that isn't restricted to class types and Reactive assumes that Base is a class (or otherwise is not going to be modified by the observable creation.)这里的基本问题是SelectableSectionType是一个不限于 class 类型的协议,并且Reactive假定Base是 class (否则不会被可观察的创建修改。)

I think the most generic you can make this is something like:我认为你可以做的最通用的是:

extension Reactive {
    func selectedValue<Row, T>() -> Observable<T?> where Base: SelectableSection<Row>, Row: SelectableRowType, T == Row.Cell.Value  {
        Observable.create { [base] observer in
            base.onSelectSelectableRow = { cell, row in
                observer.onNext(row.value) // this is problematic. See below.
            }
            return Disposables.create {
                observer.onCompleted() // this is wrong. See below.
            }
        }
    }
}

The biggest problem with the above though is that if you subscribe to the resulting Observable more than once or create more than one Observable using this computed property, all but the last subscription will silently fail.上述最大的问题是,如果您多次订阅生成的 Observable 或使用此计算属性创建多个 Observable,除了最后一个订阅之外的所有订阅都将静默失败。 The simple way to fix this is to always remember to share any result but that's rather error prone.解决此问题的简单方法是始终记住共享任何结果,但这很容易出错。

The way to fix this would be to associate a Subject with each SelectableSection, but you can't modify the class, so what are we to do?解决这个问题的方法是将一个主题与每个 SelectableSection 相关联,但是您不能修改 class,那么我们该怎么办?

Here's a solution:这是一个解决方案:

extension Reactive {
    func selectedValue<Row, T>() -> Observable<T?> where Base: SelectableSection<Row>, Row: SelectableRowType, T == Row.Cell.Value  {
        Observable.create { [base] observer in
            if let block = selectableSections.first(where: { $0.section === base }) {
                let subject = block.subject as! PublishSubject<T?>
                return Disposables.create(
                    block.disposable.retain(),
                    subject.subscribe(observer)
                )
            }
            else {
                let subject = PublishSubject<T?>()
                let block = SelectableSectionBlock(
                    section: base,
                    subject: subject,
                    disposable: RefCountDisposable(disposable: Disposables.create {
                        selectableSections.removeAll(where: { $0.section === base })
                    })
                )
                base.onSelectSelectableRow = { cell, row in
                    subject.onNext(row.value)
                }
                selectableSections.append(block)
                return Disposables.create(
                    block.disposable,
                    subject.subscribe(observer)
                )
            }
        }
    }
}

private struct SelectableSectionBlock {
    let section: Section
    let subject: Any
    let disposable: RefCountDisposable
}

private var selectableSections = [SelectableSectionBlock]()

The selectableSections array stores a Subject and RefCountDisposable for each SelectableSection. selectableSections数组存储每个 SelectableSection 的 Subject 和 RefCountDisposable。

Whenever an Observable is created, or subscribed to...每当创建或订阅 Observable 时...

  • if it's the first time working with this section, it will create a Subject and RefCountDisposable assign the onSelectSelectableRow to send a next event to the subject and store the subject in the array.如果这是第一次使用此部分,它将创建一个 Subject 并 RefCountDisposable 分配 onSelectSelectableRow 以向该主题发送下一个事件并将该主题存储在数组中。
  • otherwise it will find the subject and disposable associated with this Section and retain the disposable.否则它将找到与本节相关的主题和一次性用品并保留一次性用品。

Once it has the subject and disposable from above, it will subscribe the new observer to the subject and return a new Disposable that will remove that subscription and decrement the ref-count when the time comes.一旦它从上面获得了主题和一次性,它将为新的观察者订阅主题并返回一个新的 Disposable,它将删除该订阅并在时间到来时减少引用计数。

Yes this is quite a bit more complex than the simple assignment case, but it's the right thing to do.是的,这比简单的赋值情况要复杂得多,但这是正确的做法。

As for calling onCompleted() inside the disposable closure.至于在一次性闭包内调用onCompleted() By the time the closure is called, the observer has already emitted an onCompleted/onError event, or the observer has stopped listening to the observable.在调用闭包时,观察者已经发出了 onCompleted/onError 事件,或者观察者已经停止监听 observable。 So this event will never be seen.所以这个事件永远不会被看到。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM