当前位置: > > > Swift - RxSwift的使用详解2(响应式编程与传统式编程的比较样例)

Swift - RxSwift的使用详解2(响应式编程与传统式编程的比较样例)

在详细介绍 RxSwift 相关的知识点之前,我想先通过一个样例演示下 RxSwift 到底能做什么,好让大家能够对其有一个直观的了解。

三、两种编程方式的比较样例

1,效果图

这里我以最常见的 tableView 数据展示功能为例作为演示。后面分别使用传统写法,以及使用 RxSwift 响应式写法来实现,大家可以比较下它们的区别。
  • 表格中显示的是歌曲信息(歌名,以及歌手)
  • 点击选中任意一个单元格,在控制台中打印出对应的歌曲信息。
          

2,准备工作

首先我们创建一个 Music 的结构体,用来保存歌曲名称、歌手名字。此外它还遵循 CustomStringConvertible 协议,方便我们输出调试。
import UIKit

//歌曲结构体
struct Music {
    let name: String //歌名
    let singer: String //演唱者
    
    init(name: String, singer: String) {
        self.name = name
        self.singer = singer
    }
}

//实现 CustomStringConvertible 协议,方便输出调试
extension Music: CustomStringConvertible {
    var description: String {
        return "name:\(name) singer:\(singer)"
    }
}

3,过去我们会这么做(传统式编程)

(1)首先写一个 ViewModel
  • 这里面没有什么太复杂的东西,就是生成一个 UITableView 所使用的数据源。
import Foundation

//歌曲列表数据源
struct MusicListViewModel {
    let data = [
        Music(name: "无条件", singer: "陈奕迅"),
        Music(name: "你曾是少年", singer: "S.H.E"),
        Music(name: "从前的我", singer: "陈洁仪"),
        Music(name: "在木星", singer: "朴树"),
    ]
}

(2)视图控制器代码(ViewController.swift
  • 接着我们设置 UITableView 的委托,并让视图控制器实现 UITableViewDataSource UITableViewDelegate 协议,及相关的协议方法。
  • 这个大家肯定都写过无数遍了,也没什么好讲的。算一下,这里一共需要 43 行代码。
import UIKit
import RxSwift

class ViewController: UIViewController {

    //tableView对象
    @IBOutlet weak var tableView: UITableView!
    
    //歌曲列表数据源
    let musicListViewModel = MusicListViewModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //设置代理
        tableView.dataSource = self
        tableView.delegate = self
    }
}

extension ViewController: UITableViewDataSource {
    //返回单元格数量
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return musicListViewModel.data.count
    }
    
    //返回对应的单元格
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
        -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "musicCell")!
        let music = musicListViewModel.data[indexPath.row]
        cell.textLabel?.text = music.name
        cell.detailTextLabel?.text = music.singer
        return cell
    }
}

extension ViewController: UITableViewDelegate {
    //单元格点击
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("你选中的歌曲信息【\(musicListViewModel.data[indexPath.row])】")
    }
}

4,现在使用 RxSwift 进行改造(响应式编程)

(1)对 ViewModel 做些修改
  • 这里我们将 data 属性变成一个可观察序列对象(Observable Squence),而对象当中的内容和我们之前在数组当中所包含的内容是完全一样的。
  • 关于可观察序列对象在后面的文章中我会详细介绍。简单说就是“序列”可以对这些数值进行“订阅(Subscribe)”,有点类似于“通知(NotificationCenter)”
import RxSwift

//歌曲列表数据源
struct MusicListViewModel {
    let data = Observable.just([
        Music(name: "无条件", singer: "陈奕迅"),
        Music(name: "你曾是少年", singer: "S.H.E"),
        Music(name: "从前的我", singer: "陈洁仪"),
        Music(name: "在木星", singer: "朴树"),
    ])
}

(2)视图控制器代码(ViewController.swift
  • 这里我们不再需要实现数据源和委托协议了。而是写一些响应式代码,让它们将数据和 UITableView 建立绑定关系。
  • 算了下这里我们只需要 31 行代码,同之前的相比,一下减少了 1/4 代码量。而且代码也更清爽了些。
代码的简单说明:
  • DisposeBag:作用是 Rx 在视图控制器或者其持有者将要销毁的时候,自动释法掉绑定在它上面的资源。它是通过类似“订阅处置机制”方式实现(类似于 NotificationCenter removeObserver)。
  • rx.items(cellIdentifier:):这是 Rx 基于 cellForRowAt 数据源方法的一个封装。传统方式中我们还要有个 numberOfRowsInSection 方法,使用 Rx 后就不再需要了(Rx 已经帮我们完成了相关工作)。
  • rx.modelSelected: 这是 Rx 基于 UITableView 委托回调方法 didSelectRowAt 的一个封装。
import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {

    //tableView对象
    @IBOutlet weak var tableView: UITableView!
    
    //歌曲列表数据源
    let musicListViewModel = MusicListViewModel()
    
    //负责对象销毁
    let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //将数据源数据绑定到tableView上
        musicListViewModel.data
            .bind(to: tableView.rx.items(cellIdentifier:"musicCell")) { _, music, cell in
                cell.textLabel?.text = music.name
                cell.detailTextLabel?.text = music.singer
            }.disposed(by: disposeBag)
        
        //tableView点击响应
        tableView.rx.modelSelected(Music.self).subscribe(onNext: { music in
            print("你选中的歌曲信息【\(music)】")
        }).disposed(by: disposeBag)
    }
}
评论5
  • 5楼
    2018-09-03 14:28
    柳天

    你好 按照这些代码写的demo 无法显示tableview 报错libc++abi.dylib: terminating with uncaught exception of type NSException

    站长回复

    我测试了下是没问题的啊。你再仔细检查下是不是哪里写错了,比如tableview的@IBOutlet绑定什么的。

  • 4楼
    2018-08-23 15:45
    plutusCat

    想按照这个写 Demo 慢慢学习,Demo 地址 https://github.com/PlutusCat/RxSwift_Learning
    感谢站长提供学习教程。
    站长如果不同意,请告知,我会及时删除。

    站长回复

    转载或者其它情况只要注明出处就可以了。我也很高兴能帮助到你。

  • 3楼
    2018-02-01 09:20
    蓝灵调

    太棒了,这里的文章都很简单易懂。

    站长回复

    谢谢你的夸奖。欢迎常来看看,我会持续更新下去的。

  • 2楼
    2018-01-16 19:38
    season

    开始讲解RxSwift了呀,顶一个
    一直都是在用传统的Swift方式编程,在这里好好学习一下

    站长回复

    是啊,因为很多人点名说想看RxSwift教程,所以就写了。就是RxSwift内容比较多,后面需要比较多的篇幅才能讲完。

  • 1楼
    2018-01-16 14:45
    904785255@qq.com

    你好,按照你的操作程序直接退出

    站长回复

    你是不是没有在StoryBoard里添加tableView,并和代码做IBOutlet绑定。