iOS探索RxSwift之Subject

62 阅读2分钟

一、Subject初探

在RxsWift中还有一种非常特殊的序列 Subject - 既是序列,也是观察者,Subject是一个代理,它既是Observer,也是Observable .

1.Subject的五种类型

直接上例子:

        // PublishSubject
        // 1:初始化序列
        let publishSub = PublishSubject<Int>() //初始化一个PublishSubject 装着Int类型的序列
        // 2:发送响应序列
        publishSub.onNext(1)
        // 3:订阅序列
        publishSub.subscribe { print("订阅到了:", $0) }
            .disposed(by: disposbag)
        // 再次发送响应
        publishSub.onNext(2)
        publishSub.onNext(3)

        print("********** BehaviorSubject **********")
        // BehaviorSubject
        // 1:创建序列
        let behaviorSub = BehaviorSubject.init(value: 100)
        // 2:发送信号
        behaviorSub.onNext(2)
        behaviorSub.onNext(3)
        // 3:订阅序列
        behaviorSub.subscribe{ print("订阅到了1:", $0) }
            .disposed(by: disposbag)
        // 再次发送
        behaviorSub.onNext(4)
        behaviorSub.onNext(5)
        // 再次订阅
        behaviorSub.subscribe{ print("订阅到了:", $0) }
            .disposed(by: disposbag)
            

        print("********** ReplaySubject **********")
        // ReplaySubject
        // 1:创建序列
        let replaySub = ReplaySubject<Int>.create(bufferSize: 2)
        // let replaySub = ReplaySubject<Int>.createUnbounded()
        // 2:发送信号
        replaySub.onNext(1)
        replaySub.onNext(2)
        replaySub.onNext(3)
        replaySub.onNext(4)
        // 3:订阅序列
        replaySub.subscribe{ print("订阅到了:", $0) }
            .disposed(by: disposbag)
        // 再次发送
        replaySub.onNext(7)
        replaySub.onNext(8)
        replaySub.onNext(9)

        print("********** AsyncSubject **********")
        // AsyncSubject
        // 1:创建序列
        let asynSub = AsyncSubject<Int>.init()
        // 2:发送信号
        asynSub.onNext(1)
        asynSub.onNext(2)
        // 3:订阅序列
        asynSub.subscribe{ print("订阅到了:", $0) }
            .disposed(by: disposbag)
        // 再次发送
        asynSub.onNext(3)
        asynSub.onNext(4)
        asynSub.onCompleted()


        print("********** Variable **********")
        // Variable : 5.0已经废弃(BehaviorRelay 替换) - 大家可以了解一下
        // 1:创建序列
        let variableSub = BehaviorRelay.init(value: 1)
        // 2:发送信号
        variableSub.accept(100)
        variableSub.accept(10)
        // 3:订阅信号
        variableSub.asObservable().subscribe{ print("订阅到了:", $0) }
            .disposed(by: disposbag)
        print("打印:\(variableSub.value)")
        // 再次发送
        variableSub.accept(1000)

查看运行结果: image.png

  1. PublishSubject:可以不需要初始值来进行初始化(也就是可以为空),并且它只会向订阅者发送在订阅之后才接收到的元素。
  2. BehaviorSubject:通过一个默认初始值来创建,当订阅者订阅BehaviorSubject时,会收到订阅后Subject上一个发出的Event,如果还没有收到任何数据,会发出一个默认值,之后就和PublishSubject一样,正常接收新的事件。
  3. ReplaySubject:ReplaySubject发送源Observable的所有事件,无论observer什么时候开始订阅。bufferSize: 2 在订阅之前保留2次。
  4. AsyncSubject:AsyncSubject只发送由源Observable发送的最后一个事件,并且只在源Observable完成之后(如果源Observable没有发生任何值,AsyncSubject也不会发送任何值)。
  5. BehaviorRelay-Variable:替换原来的Variable,可以存储一个信号,随时订阅响应,behaviorRelay.accept(20)。(后面废弃是因为,违背了响应式编程)
  • 首先他们都是 Observable,他们的订阅者都能收到他们发出的新的 Event
  • 直到 Subject 发出 .complete 或者 .errorEvent 后,该 Subject 便终结了,同时它也就不会再发出.next事件。
  • 对于那些在 Subject 终结后再订阅他的订阅者,也能收到 subject发出的一条 .complete.errorevent,告诉这个新的订阅者它已经终结了。
  • 他们之间最大的区别只是在于:当一个新的订阅者刚订阅它的时候,能不能收到 Subject 以前发出过的旧 Event,如果能的话又能收到多少个。

2.Subject的绑定/传值

我们通过一个实战例子来学习:

var modelsSub: BehaviorSubject<[LGModel]>?   //定义BehaviorSubject

//初始化BehaviorSubject
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()

        modelItems = LGCacheManager.manager.fetchLGModelData()
        modelsSub = BehaviorSubject(value: modelItems) //设置默认值-重启app时选中状态
        modelsSub?.subscribe(onNext: { items in
            self.modelItems = items
            self.tableView.reloadData()
            LGCacheManager.manager.updateAllData(models: items)
            self.deleteBtn.isEnabled = !items.isEmpty
            let num = items.filter { model in
                !model.isFinished
            }.count
            self.navigationItem.rightBarButtonItem?.isEnabled = num < 6
            self.title = num == 0 ? "RxSwiftDemo" : "还有\(num)条待办事项"
        }).disposed(by: disposeBag)
    }

订阅绑定items消息,更新updateAllData 数据,isFinished 查询完成数量。更新UI等操作。

    @objc func didClickAddAction() {
        let newRowIndex = modelItems.count
        let model = LGModel(title:"我是一条数据:\(Date().timeIntervalSince1970)", isFinished: false)
        modelItems.append(model)
        modelsSub?.onNext(modelItems)
    }

添加一条数据,或者发送onnext模型消息。

extension LGViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        // 点击选择就是删除
        // 处理数据
        modelItems.remove(at: indexPath.row)
        modelsSub?.onNext(modelItems)
        tableView.deselectRow(at: indexPath, animated: true)
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let model = modelItems[indexPath.row]
        model.toggleFinished()
        modelsSub?.onNext(modelItems)
    }
}

点击选择改变cell model状态,及删除功能。都是通过向BehaviorSubject发送.onNext(modelItems)消息。

image.png 传值:

fileprivate func pushToDetailVC(model: LGModel?) {
        let sb = UIStoryboard(name: "Main", bundle: nil)
        detailVC = (sb.instantiateViewController(withIdentifier: "LGDetailViewController") as! LGDetailViewController)
        if let m = model {
            detailVC?.model = m
        }
        detailVC?.todoOB.subscribe(onNext: { item in
            self.modelItems.append(item)
            self.modelsSub?.onNext(self.modelItems)
        }).disposed(by: disposeBag)
        navigationController?.pushViewController(detailVC!, animated: true)
}

外部订阅的方式(LGDetailViewController):

    fileprivate let todoSubject = PublishSubject<LGModel>()  //私有
    var todoOB: Observable<LGModel> {//外界只有订阅的权利,里面才能响应
        todoSubject.asObservable()
    }