Swift第十三章 面向协议编程、响应式编程

面向协议编程

  1. 面向协议编程(Protocol Oriented Programming,简称POP)
    1. 是Swift的一种编程范式, Apple于2015年WWDC提出
    2. 在Swift的标准库中,能见到大量POP的影子
  2. 同时,Swift也是一门面向对象的编程语言(Object Oriented Programming,简称OOP)
    1. 在Swift开发中,OOP和POP是相辅相成的,任何一方并不能取代另一方
  3. POP能弥补OOP一些设计上的不足

OOP与POP

  1. OOP的三大特性:封装、继承、多态
  2. 继承的经典使用场合
    1. 当多个类(比如A、B、C类)具有很多共性时,可以将这些共性抽取到一个父类中(比如D类),最后A、B、C类继承D类
  3. OOP的不足
    1. 但有些问题,使用OOP并不能很好解决,比如:如何将 BVC、DVC 的公共方法 run 抽取出来?

       class BVC: UIViewController {
           func run() {
               print("run")
           }
       }
       class DVC: UITableViewController {
           func run() {
               print("run")
           }
       }
      
    2. 基于OOP想到的一些解决方案?

      1. 将run方法放到另一个对象A中,然后BVC、DVC拥有对象A属性; 缺点: 多了一些额外的依赖关系
      2. 将run方法增加到UIViewController分类中; 缺点:UIViewController会越来越臃肿,而且会影响它的其他所有子类
      3. 将run方法抽取到新的父类,采用多继承?(C++支持多继承)缺点:会增加程序设计复杂度,产生菱形继承等问题,需要开发者额外解决
  4. POP的解决方案

     protocol Runnable {
         func run()
     }
     extension Runnable {
         func run() {
             print("run")
         }
     }
     class BVC: UIViewController, Runnable {}
     class DVC: UITableViewController, Runnable {}
    

POP的注意点

  1. 优先考虑创建协议,而不是父类(基类)
  2. 优先考虑值类型(struct、enum),而不是引用类型(class)
  3. 巧用协议的扩展功能
  4. 不要为了面向协议而使用协议

利用协议实现前缀效果

  1. 举例

     var string = "123fdsf434"
     print(string.mj.numberCount())
        
     struct MJ<Base> {
         let base: Base
         init(_ base: Base) {
             self.base = base
         }
     }
        
     protocol MJCompatible {}
     extension MJCompatible {
         static var mj: MJ<Self>.Type {
             get { MJ<Self>.self }
             set {}
         }
         var mj: MJ<Self> {
             get { MJ(self) }
             set {}
         }
     }
     extension String: MJCompatible {}
     extension MJ where Base == String {
         func numberCount() -> Int {
             var count = 0
             for c in base where ("0"..."9").contains(c) {
                 count += 1
             }
             return count
         }
     }
    
  2. Base: 类

     class Person {}
     class Student: Person {}
     extension Person: MJCompatible {}
     extension MJ where Base: Person {
         func run() {}
         static func test() {}
     }
        
     Person.mj.test()
     Student.mj.test()
     let p = Person()
     p.mj.run()
     let s = Student()
     s.mj.run()
    
  3. Base: 协议

     var s1: String = "123fdsf434"
     var s2: NSString = "123fdsf434"
     var s3: NSMutableString = "123fdsf434"
     print(s1.mj.numberCount())
     print(s2.mj.numberCount())
     print(s3.mj.numberCount())
        
     extension String: MJCompatible {}
     extension NSString: MJCompatible {}
     extension MJ where Base: ExpressibleByStringLiteral {
         func numberCount() -> Int {
             let string = base as! String
             var count = 0
             for c in string where ("0"..."9").contains(c) {
                 count += 1
             }
             return count
         }
     }
    

利用协议实现类型判断

//判断是不是数组
func isArray(_ value: Any) -> Bool { value is [Any] }
isArray( [1, 2] )
isArray( ["1", 2] )
isArray( NSArray() )
isArray( NSMutableArray() )

//判断是不是数组类型,自定义一个协议,让数组类型遵守这个协议,然后判断,只要遵守这个自定义协议的类型都是数组类型
protocol ArrayType {}
extension Array: ArrayType {} //数组遵守协议ArrayType
extension NSArray: ArrayType {} 
//凡是遵守了ArrayType协议的类型都是数组
func isArrayType(_ type: Any.Type) -> Bool { type is ArrayType.Type }
isArrayType([Int].self) //true
isArrayType([Any].self)//true
isArrayType(NSArray.self)//true
isArrayType(NSMutableArray.self)//true

响应式编程

简介

  1. 响应式编程(Reactive Programming,简称RP)
    1. 也是一种编程范式,于1997年提出,可以简化异步编程,提供更优雅的数据绑定
    2. 一般与函数式融合在一起,所以也会叫做:函数响应式编程(Functional Reactive Programming,简称FRP)
  2. 比较著名的、成熟的响应式框架
    1. ReactiveCocoa
    2. 简称RAC,有Objective-C、Swift版本
    3. 官网: http://reactivecocoa.io/
    4. github:https://github.com/ReactiveCocoa
  3. ReactiveX
    1. 简称Rx,有众多编程语言的版本,比如RxJava、RxKotlin、RxJS、RxCpp、RxPHP、RxGo、RxSwift等等
    2. 官网: http://reactivex.io/
    3. github: https://github.com/ReactiveX

RxSwift

  1. RxSwift(ReactiveX for Swift),ReactiveX的Swift版本
    1. 源码:https://github.com/ReactiveX/RxSwift
    2. 中文文档: https://beeth0ven.github.io/RxSwift-Chinese-Documentation/
  2. RxSwift的github上已经有详细的安装教程,这里只演示CocoaPods方式的安装

     //1. Podfile
     use_frameworks!
     target 'target_name' do
     pod 'RxSwift', '~> 5'
     pod 'RxCocoa', '~> 5'
     end
        
     //2. 命令行
     p pod repo update
     p pod install
        
     //3. 导入模块
     import RxSwift
     import RxCocoa
    
  3. 模块说明
    1. RxSwift:Rx标准API的Swift实现,不包括任何iOS相关的内容
    2. RxCocoa:基于RxSwift,给iOS UI控件扩展了很多Rx特性

RxSwift的核心角色

  1. Observable:负责发送事件(Event)
  2. Observer:负责订阅Observable,监听Observable发送的事件(Event)
  3. Event有3种
    1. next:携带具体数据
    2. error:携带错误信息,表明Observable终止,不会再发出事件
    3. completed:表明Observable终止,不会再发出事件
     public enum Event<Element> {
         /// Next element is produced.
         case next(Element)
         /// Sequence terminated with an error.
         case error(Swift.Error)
         /// Sequence completed successfully.
         case completed
     }
    

创建、订阅Observable

  1. 创建Observable

     var observable = Observable<Int>.create { observer in
     observer.onNext(1)
     observer.onCompleted()
     return Disposables.create()
     }
     // 等价于
     observable = Observable.just(1)
     observable = Observable.of(1)
     observable = Observable.from([1])
        
        
     var observable = Observable<Int>.create { observer in
     observer.onNext(1)
     observer.onNext(2)
     observer.onNext(3)
     observer.onCompleted()
     return Disposables.create()
     }
     // 等价于
     observable = Observable.of(1, 2, 3)
     observable = Observable.from([1, 2, 3])
    
  2. 订阅

     //方式1:
     observable.subscribe { event in
     print(event)
     }.dispose()
        
     //方式2:
     observable.subscribe(onNext: {
     print("next", $0)
     }, onError: {
     print("error", $0)
     }, onCompleted: {
     print("completed")
     }, onDisposed: {
     print("dispose")
     }).dispose()
    
  3. 其他方式:

     //参数: 每3s执行,period中间间隔1s,scheduler:主线程执行
     let observable = Observable<Int>.timer(.seconds(3),period:.seconds(1),scheduler: MainScheduler.instance)
        
        
     //监听bind也可以订阅,也是一个订阅者
     let bag = DisposeBag()
     observable.map { "数值是\($0)" }
     .bind(to: label.rx.text)
     .disposed(by: bag)
    

创建Observer

  1. 方式1:

     let observer = AnyObserver<Int>.init { event in
         switch event {
             case .next(let data):
             print(data)
             case .completed:
             print("completed")
             case .error(let error):
             print("error", error)
         }
     }
     Observable.just(1).subscribe(observer).dispose()
    
  2. 方式2:

     //binder就是observer类型
     let binder = Binder<String>(label) { label, text in
         label.text = text
     }
     Observable.just(1).map { "数值是\($0)" }.subscribe(binder).dispose()
        
     Observable.just(1).map { "数值是\($0)" }.bind(to: binder).dispose()
    
  3. 扩展Binder属性

     extension Reactive where Base: UIView {
         var hidden: Binder<Bool> {
             Binder<Bool>(base) { view, value in
                 view.isHidden = value
             }
         }
     }
        
     let observable = Observable<Int>.interval(.seconds(1),
     scheduler: MainScheduler.instance)
        
     observable.map { $0 % 2 == 0 }.bind(to: button.rx.hidden).disposed(by: bag)
    

Disposable

  1. 每当Observable被订阅时,都会返回一个Disposable实例,当调用Disposable的dispose,就相当于取消订阅
  2. 在不需要再接收事件时,建议取消订阅,释放资源。有3种常见方式取消订阅

     // 立即取消订阅(一次性订阅)
     observable.subscribe { event in
         print(event)
     }.dispose()
        
     // 当bag销毁(deinit)时,会自动调用Disposable实例的dispose
     observable.subscribe { event in
         print(event)
     }.disposed(by: bag)
     // self销毁时(deinit)时,会自动调用Disposable实例的dispose
     let _ = observable.takeUntil(self.rx.deallocated).subscribe { event in
         print(event)
     }
    

传统的状态监听

  1. 在开发中经常要对各种状态进行监听,传统的常见监听方案有
    1. KVO
    2. Target-Action
    3. Notification
    4. Delegate
    5. Block Callback
  2. 传统方案经常会出现错综复杂的依赖关系、耦合性较高,还需要编写重复的非业务代码

RxSwift的状态监听

  1. 方式1

     button.rx.tap.subscribe(onNext: {
         print("按钮被点击了1")
     }).disposed(by: bag)
        
     let data = Observable.just([
         Person(name: "Jack", age: 10),
         Person(name: "Rose", age: 20)
     ])
     data.bind(to: tableView.rx.items(cellIdentifier: "cell")) { row, person, cell in
         cell.textLabel?.text = person.name
         cell.detailTextLabel?.text = "\(person.age)"
     }.disposed(by: bag)
     tableView.rx.modelSelected(Person.self)
     .subscribe(onNext: { person in
         print("点击了", person.name)
     }).disposed(by: bag)
    
  2. 方式2

     class Dog: NSObject {
         @objc dynamic var name: String?
     }
     dog.rx.observe(String.self, "name")
     .subscribe(onNext: { name in
         print("name is", name ?? "nil")
     }).disposed(by: bag)
        
     dog.name = "larry"
     dog.name = "wangwang"
        
     NotificationCenter.default.rx
     .notification(UIApplication.didEnterBackgroundNotification)
     .subscribe(onNext: { notification in
         print("APP进入后台", notification)
     }).disposed(by: bag)
    

既是Observable,又是Observer

  1. 举例

     Observable.just(0.8).bind(to: slider.rx.value).dispose()
     slider.rx.value.map {
         "当前数值是:\($0)"
     }.bind(to: textField.rx.text).disposed(by: bag)
     textField.rx.text
     .subscribe(onNext: { text in
         print("text is", text ?? "nil")
     }).disposed(by: bag)
    
  2. 诸如UISlider.rx.value、UTextField.rx.text这类属性值,既是Observable,又是Observer
  3. 它们是RxCocoa.ControlProperty类型
Table of Contents