9

我为 1 UIButton 订阅了 2 次:

  1. 首次订阅,每次点击更新 UI
  2. 第二次订阅,用于在累积点击后每 1 秒更新一次 Web Service 上的值。

代码:

class ProductionSize {
    var id : Int?
    var size: Int = 0
    var name: String = ""
}

class ProductionCell: UICollectionViewCell {
    var rxBag = DisposeBag()


    // this will be set in the (cellForItemAt indexPath: IndexPath) of collection view
    var productionSize: ProductionSize? {
        didSet {
            showProductionSize()
            prepareButton()
        }
    }

    func showProductionSize() {
        // ... code for showing ProductionSize in labels
    }

    func prepareButton() {
        // This for subscribing for every click for displaying purpose

        btn_increase.rx.tap
            .subscribe(){event in 
                self.increaseClicked() 
            }
            .addDisposableTo(rxBag)

        // this for subscribing for sending webservice request after 1 second of clicking the button (so that if user click it quickly i send only last request)

        btn_increase.rx.tap
            .debounce(1.0, scheduler: MainScheduler.instance)
            .subscribe(){ event in self.updateOnWS() }
            .addDisposableTo(rxBag)
    }

    func increaseClicked() {
        productionSize.size = productionSize.size + 1
        showProductionSize()
    }

    func updateOnWS() {
        // code for updating on webservice with Moya, RxSwift and Alamofire§
    }


    // when scrolling it gets called to dispose subscribtions
    override func prepareForReuse() {
        rxBag = DisposeBag()
    }

}

问题:

由于处置发生在prepareForReuse(),如果我多次单击该按钮并立即滚动,则 Web 服务调用将被处置且未更新。

我尝试过的:

  1. 添加addDisposableTo(vc?.rx_disposableBag)到父 ViewController DisposableBag。

    问题是,订阅的累积和每次点击都会被updateWS()调用多次,每次滚动都订阅了并且从未处理过。

  2. 我试图从prepareForReuse().

    问题是,按钮的订阅再次被重复和累积,并且每次点击都会调用许多 Web 服务调用。

问题: 我怎样才能让debounce订阅被调用到最后并且从不重复多个订阅(在addDisposableToviewController Bag 的情况下)?

4

2 回答 2

11

由于prepareButton()总是在集合视图的 (cellForItemAt indexPath: IndexPath) 中调用,你可以试试这个:

func prepareButton() {

    self.rxBag = nil 
    let rxBag = DisposeBag()

    // This for subscribing for every click for displaying purpose

    btn_increase.rx.tap
        .subscribe(onNext: { [weak self] _ in 
            self?.increaseClicked()
        })
        .addDisposableTo(rxBag)

    // this for subscribing for sending webservice request after 1 second of clicking the button (so that if user click it quickly i send only last request)

    btn_increase.rx.tap
        .debounce(1.0, scheduler: MainScheduler.instance)
        .subscribe(onNext: { [weak self] _ in 
            self?.updateOnWS()
        })
        .addDisposableTo(rxBag)

    self.rxBag = rxBag
}

删除prepareForReuse()实现。

于 2017-04-11T15:33:54.597 回答
7

添加addDisposableTo(vc?.rx_disposableBag)到父 ViewController DisposableBag。

问题是,订阅的累积和每次点击都会调用 updateWS() 多次,它在每次滚动时都被订阅并且从未处理过。

由于您订阅按钮的点击方式,您可能self.updateOnWS()会被多次调用。

    btn_increase.rx.tap
        .debounce(1.0, scheduler: MainScheduler.instance)
        .subscribe(){ event in self.updateOnWS() }
        .addDisposableTo(rxBag)

如您所见,您使用该subscribe()方法订阅了所有事件。这意味着所有 Rx 事件(onNextonErroronCompletedonSubscribedonDisposed)都会触发self.updateOnWS(). 您可以通过打印出event对象以查看触发了什么事件来检查是否是这种情况。

onNext仅订阅

一个可能的解决方法可能是只订阅该onNext操作。

    btn_increase.rx.tap
        .debounce(1.0, scheduler: MainScheduler.instance)
        .subscribe(onNext: { [weak self] (_ : Void) in
               self?.updateOnWS() 
        })
        .addDisposableTo(vc?.rxdisposableBag)

通过使用DisposeBag视图控制器,即使单元格被释放(向下滚动时),您也可以确保操作仍然继续。但是,如果您需要它在处理单元格时处理订阅,请使用DisposeBag单元格的,而不是视图控制器。

旁注 - 内存泄漏

请注意,对的引用self被指定为弱引用,这样您就可以防止发生内存泄漏。通过将其指定为弱,它将为您提供对 self 的引用,该引用是可选的。

如果不这样做,您为块创建的闭包onNext将保留对selfwhich is your的强引用UICollectionViewCell,而后者又拥有我们正在讨论的闭包。

这最终会导致内存不足崩溃。请参阅我在您的问题的评论中发布的参考资料,以了解更多关于错误引用 self 导致的内存泄漏的信息。

于 2017-04-04T16:20:02.607 回答