Swift第十三章 面向协议编程、响应式编程
面向协议编程
- 面向协议编程(Protocol Oriented Programming,简称POP)
- 是Swift的一种编程范式, Apple于2015年WWDC提出
- 在Swift的标准库中,能见到大量POP的影子
- 同时,Swift也是一门面向对象的编程语言(Object Oriented Programming,简称OOP)
- 在Swift开发中,OOP和POP是相辅相成的,任何一方并不能取代另一方
- POP能弥补OOP一些设计上的不足
OOP与POP
- OOP的三大特性:封装、继承、多态
- 继承的经典使用场合
- 当多个类(比如A、B、C类)具有很多共性时,可以将这些共性抽取到一个父类中(比如D类),最后A、B、C类继承D类
- OOP的不足
-
但有些问题,使用OOP并不能很好解决,比如:如何将 BVC、DVC 的公共方法 run 抽取出来?
class BVC: UIViewController { func run() { print("run") } } class DVC: UITableViewController { func run() { print("run") } } -
基于OOP想到的一些解决方案?
- 将run方法放到另一个对象A中,然后BVC、DVC拥有对象A属性; 缺点: 多了一些额外的依赖关系
- 将run方法增加到UIViewController分类中; 缺点:UIViewController会越来越臃肿,而且会影响它的其他所有子类
- 将run方法抽取到新的父类,采用多继承?(C++支持多继承)缺点:会增加程序设计复杂度,产生菱形继承等问题,需要开发者额外解决
-
-
POP的解决方案
protocol Runnable { func run() } extension Runnable { func run() { print("run") } } class BVC: UIViewController, Runnable {} class DVC: UITableViewController, Runnable {}
POP的注意点
- 优先考虑创建协议,而不是父类(基类)
- 优先考虑值类型(struct、enum),而不是引用类型(class)
- 巧用协议的扩展功能
- 不要为了面向协议而使用协议
利用协议实现前缀效果
-
举例
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 } } -
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() -
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
响应式编程
简介
- 响应式编程(Reactive Programming,简称RP)
- 也是一种编程范式,于1997年提出,可以简化异步编程,提供更优雅的数据绑定
- 一般与函数式融合在一起,所以也会叫做:函数响应式编程(Functional Reactive Programming,简称FRP)
- 比较著名的、成熟的响应式框架
- ReactiveCocoa
- 简称RAC,有Objective-C、Swift版本
- 官网: http://reactivecocoa.io/
- github:https://github.com/ReactiveCocoa
- ReactiveX
- 简称Rx,有众多编程语言的版本,比如RxJava、RxKotlin、RxJS、RxCpp、RxPHP、RxGo、RxSwift等等
- 官网: http://reactivex.io/
- github: https://github.com/ReactiveX
RxSwift
- RxSwift(ReactiveX for Swift),ReactiveX的Swift版本
- 源码:https://github.com/ReactiveX/RxSwift
- 中文文档: https://beeth0ven.github.io/RxSwift-Chinese-Documentation/
-
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 - 模块说明
- RxSwift:Rx标准API的Swift实现,不包括任何iOS相关的内容
- RxCocoa:基于RxSwift,给iOS UI控件扩展了很多Rx特性
RxSwift的核心角色
- Observable:负责发送事件(Event)
- Observer:负责订阅Observable,监听Observable发送的事件(Event)
- Event有3种
- next:携带具体数据
- error:携带错误信息,表明Observable终止,不会再发出事件
- 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
-
创建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]) -
订阅
//方式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() -
其他方式:
//参数: 每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:
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:
//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() -
扩展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
- 每当Observable被订阅时,都会返回一个Disposable实例,当调用Disposable的dispose,就相当于取消订阅
-
在不需要再接收事件时,建议取消订阅,释放资源。有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) }
传统的状态监听
- 在开发中经常要对各种状态进行监听,传统的常见监听方案有
- KVO
- Target-Action
- Notification
- Delegate
- Block Callback
- 传统方案经常会出现错综复杂的依赖关系、耦合性较高,还需要编写重复的非业务代码
RxSwift的状态监听
-
方式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
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
-
举例
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) - 诸如UISlider.rx.value、UTextField.rx.text这类属性值,既是Observable,又是Observer
- 它们是RxCocoa.ControlProperty类型