一、简介
在iOS平台使用响应式编程(Functional Reactive Programming,简称FRP),有一个很好的开源项目ReactiveCocoa,ReactiveCocoa简称RAC,它是基于响应式编程(FRP)思想的Objective-C实践。
ReactiveCocoa是github开源的一个第三方框架,响应式编程的代表。通过函数式响应式编程的思想,实现的信号机制。使用ReactiveCocoa去搭建MVVM架构,使代码更加高聚合,低耦合。便捷的实现双向绑定的效果。
二、集成
- swift项目
pod 'ReactiveCocoa'
- OC项目
pod 'ReactiveObjC'
- swift与OC混编
target 'xxxxxx' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for xxxxxx
pod 'ReactiveObjC'
pod 'ReactiveCocoa'
pod 'ReactiveObjCBridge'
end
二、UIButton事件监听
import ReactiveCoccoa
bt.reactive.controlEvents(UIControl.Event.touchUpInside).observeValues { (_) in
print("test")
}
三、TextField值变化事件
import ReactiveCoccoa
textFiled.reactive.continuousTextValues.observeValues { (str) in
print(str)
}
textFiled.reactive.controlEvents(UIControl.Event.editingChanged).observeValues { (t) in
print(t.text)
}
// 输入字符长度大于3
textFiled.reactive.continuousTextValues.filter { (str) -> Bool in
let lenth = str.lengthOfBytes(using: String.Encoding.utf8)
return lenth > 3
}.observeValues { (text) in
print(text)
}
四、RACSiganl信号类
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// 创建信号
let singal = RACSignal<AnyObject>.createSignal({ (sub) -> RACDisposable? in
// 信号内部发送数据
sub.sendNext(1)
// 如果信号被取消,就必须返回一个RACDisposable
return RACDisposable(block: {
print("取消了")
})
})
// 只要订阅信号,就会返回一个被动取消信号的类
let disposable = singal.subscribeNext { (obj) in
print(obj)
}
// 主动取消订阅
disposable.dispose()
}
}
五、RACSubject :信号提供者
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// 创建信号
let subject = RACSubject<AnyObject>()
// 订阅信号
subject.subscribeNext { (obj) in
print(obj)
}
// 发送信号
subject.sendNext(123 as AnyObject)
}
}
六、RACReplaySubject:重复提供信号类
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// 创建信号
let replaySubject = RACReplaySubject<AnyObject>(capacity: 1)
// 第一个订阅信号
replaySubject.subscribeNext { (obj) in
print("test1 === \(obj)")
}
// 第二个订阅信号
replaySubject.subscribeNext { (obj) in
print("test2 === \(obj)")
}
replaySubject.sendNext(123 as AnyObject)
}
}
七、RACSequence:RAC中的集合类
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// 必须用OC类
let numbers:NSArray = [1,2,3,4,5]
numbers.rac_sequence.signal().subscribeNext { (obj) in
print(obj)
}
// 必须用OC类
let dict:NSDictionary = [
"name":"chenfan",
"age":"666"
]
dict.rac_sequence.signal().subscribeNext { (obj) in
print(obj?.first)
print(obj?.second)
}
}
}
八、常见用法
- 代理
// Student.m
#import "Student.h"
@implementation Student
- (void)test {
NSLog(@"sds");
}
@end
-----------------------------------------------------
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// 注意:Student类必须是OC类才有用!!!!!!!!!
let student = Student()
// 先注册
student.rac_signal(for: #selector(Student.test)).subscribeNext { (t) in
print("test")
}
// 后发送指令
student.test()
}
}
- KVO
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Student : NSObject
@property (nonatomic) NSString * name;
@end
NS_ASSUME_NONNULL_END
-----------------------------------------------------
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// 注意:Student类必须是OC类
let p = Student()
// 监听
p.rac_values(forKeyPath: NSStringFromSelector(#selector(getter: Student.name)), observer: self).subscribeNext { (obj) in
print(obj)
}
// 变更
p.name = "ssssss"
}
}
- 监听按钮
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
@IBOutlet weak var bt: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
bt.rac_signal(for: UIControl.Event.touchUpInside).subscribeNext { (control) in
print("test")
}
}
}
- 监听通知
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
NotificationCenter.default.rac_addObserver(forName: UIResponder.keyboardWillShowNotification.rawValue, object: nil).subscribeNext { (noti) in
print("noti?.name")
}
}
}
- 监听textfield的输入内容
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
@IBOutlet weak var textFiled: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
textFiled.rac_textSignal().subscribeNext { (str) in
print(str)
}
}
}
- Timer定时器
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
RACSignal<AnyObject>.interval(2 as Double, on: RACScheduler.mainThread()).subscribeNext { (date) in
print(date)
}
}
}
九、RAC操作方法之映射:flattenMap,Map
- flattenMap:把源信号的内容映射成一个新的信号,信号可以是任意类型
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// 创建信号
let subject = RACSubject<AnyObject>()
// 绑定信号
let bindSignal = subject.flattenMap { (obj) -> RACSignal<AnyObject>? in
// 修改源信号
return RACReturnSignal.return("obj" as AnyObject)
}
// 订阅绑定信号
bindSignal.subscribeNext { (obj) in
print(obj)
}
subject.sendNext(456 as AnyObject)
}
}
- flattenMap实现:信号中信号取值
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let signalOfsignals = RACSubject<AnyObject>()
let signal = RACSubject<AnyObject>()
let bindSignal = signalOfsignals.flattenMap { (obj) -> RACSignal<AnyObject>? in
return obj as? RACSignal<AnyObject>
}
bindSignal.subscribeNext { (obj) in
print(obj)
}
signalOfsignals.sendNext(signal)
signal.sendNext(123 as AnyObject)
}
}
- map:把源信号的值映射成一个新的值
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let subject = RACSubject<AnyObject>()
let bindSignal = subject.map { (obj) -> Any? in
return "123"
}
bindSignal.subscribeNext { (obj) in
print(obj)
}
subject.sendNext(456 as AnyObject)
}
}
十、ReactiveCocoa操作方法之组合
- concat: 按一定顺序拼接信号,当多个信号发出的时候,有顺序的接收信号
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let siganlA = RACSignal<AnyObject>.createSignal { (sub) -> RACDisposable? in
sub.sendNext("上部分数据")
sub.sendCompleted()
return nil
}
let siganlB = RACSignal<AnyObject>.createSignal { (sub) -> RACDisposable? in
sub.sendNext("下部分数据")
return nil
}
let concatSignal = siganlA.concat(siganlB)
concatSignal.subscribeNext { (obj) in
print(obj)
}
}
}
- then: 用于连接两个信号,当第一个信号完成,才会连接then返回的信号
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let siganlA = RACSignal<AnyObject>.createSignal { (sub) -> RACDisposable? in
sub.sendNext("上部分数据")
sub.sendCompleted()
return nil
}
let siganlB = RACSignal<AnyObject>.createSignal { (sub) -> RACDisposable? in
sub.sendNext("下部分数据")
return nil
}
let thenSiganl = siganlA.then { () -> RACSignal<AnyObject> in
return siganlB
}
thenSiganl.subscribeNext { (obj) in
print(obj)
}
}
}
- merge:把多个信号合并为一个信号,任何一个信号有新值的时候就会调用
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let signalA = RACSubject<AnyObject>()
let signalB = RACSubject<AnyObject>()
let mergeSiganl = signalA.merge(signalB)
mergeSiganl.subscribeNext { (obj) in
print(obj)
}
signalB.sendNext("下部分" as AnyObject)
signalA.sendNext("上部分" as AnyObject)
}
}
- zipWith:把两个信号压缩成一个信号,只有当两个信号同时发出信号内容时,并且把两个信号的内容合并成一个元组,才会触发压缩流的next事件
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let signalA = RACSubject<AnyObject>()
let signalB = RACSubject<AnyObject>()
let zipSignal = signalA.zip(with: signalB)
zipSignal.subscribeNext { (obj) in
print(obj)
}
signalA.sendNext(1 as AnyObject)
signalB.sendNext(2 as AnyObject)
}
}
十一、ReactiveCocoa操作方法之过滤
- filter:过滤信号,使用它可以获取满足条件的信号
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
@IBOutlet weak var text1: UITextField!
@IBOutlet weak var text2: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
text1.rac_textSignal().filter { (str) -> Bool in
return (str?.length ?? 0) > 3
}.subscribeNext { (str) in
print(str)
}
}
}
- ignore:忽略完某些值的信号
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let subject = RACSubject<AnyObject>()
let ignoreSignal = subject.ignore("1" as AnyObject)
ignoreSignal.subscribeNext { (obj) in
print(obj)
}
subject.sendNext("13" as AnyObject)
}
}
- distinctUntilChanged:当上一次的值和当前的值有明显的变化就会发出信号,否则会被忽略掉。
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let subject = RACSubject<AnyObject>()
subject.distinctUntilChanged().subscribeNext { (obj) in
print(obj)
}
subject.sendNext(1 as AnyObject)
subject.sendNext(1 as AnyObject)
subject.sendNext(2 as AnyObject)
subject.sendNext(1 as AnyObject)
}
}
- skip:(NSUInteger):跳过几个信号,不接受
import UIKit
import ReactiveObjC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let subject = RACSubject<AnyObject>()
subject.skip(2).subscribeNext { (obj) in
print(obj)
}
subject.sendNext(1 as AnyObject)
subject.sendNext(2 as AnyObject)
subject.sendNext(3 as AnyObject)
}
}
十二、RAC常见宏
- RAC (swift 中不可用)
#import "ViewController.h"
#import <ReactiveObjC/ReactiveObjC.h>
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *lb;
@property (weak, nonatomic) IBOutlet UITextField *text1;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
RAC(self.lb, text) = self.text1.rac_textSignal;
}
@end
- RACObserve (swift 中不可用)
#import "ViewController.h"
#import <ReactiveObjC/ReactiveObjC.h>
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *lb;
@property (weak, nonatomic) IBOutlet UITextField *text1;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[RACObserve(self.text1, frame) subscribeNext:^(id _Nullable x) {
NSLog(@"test %@",x);
}];
self.text1.frame = CGRectMake(0, 0, 100, 100);
}
@end
网友评论