ReactiveCocoa是iOS開發(fā)的一個開源第三方框架,又被稱作函數(shù)響應(yīng)是編程框架。它結(jié)合了函數(shù)式編程和響應(yīng)式編程兩種編程風(fēng)格,定義了一個標(biāo)準(zhǔn)的接口來響應(yīng)諸如點擊按鈕,網(wǎng)絡(luò)請求完成等事件。
函數(shù)式編程&響應(yīng)式編程
- 何謂函數(shù)式編程
函數(shù)式編程就是把操作任務(wù)盡量寫成一系列嵌套的函數(shù)或方法來調(diào)用。
這種編程思想有幾個特點:方法的返回值是他本身,參數(shù)是Block,如果對block不太熟悉的同學(xué),可以先看一下這篇文章:iOS Block,做一下了解。
- 何謂響應(yīng)式編程
我們可以用KVO來理解響應(yīng)式編程。一個類中有一個user對象,user有“userId”“userName”兩個屬性,我們利用KVO來觀察user的userName屬性,當(dāng)userName屬性變化了的時候,會執(zhí)行下面的方法。
#import "HomeViewController.h"
#import "User.h"
@interface HomeViewController ()
@property (nonatomic, strong) User *user;
@end
@implementation HomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.user = [User new];
self.user.userName = @"張三";
self.user.userId = 10086;
[self.user addObserver:self forKeyPath:@"userName" options:
|NSKeyValueObservingOptionNew context:nil];
NSLog(@"%@", self.user);
self.user.userName = @"李四";
self.user.userId = 10010;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"%@%ld", self.user.userName, self.user.userId);
}
添加ReactiveCocoa到工程
和其他的第三方一樣,添加ReactiveCocoa的方式有兩種:
- 通過CocoaPods添加
#RAC
pod 'ReactiveCocoa', '~> 2.5'
- 通過GitHub手動下載ReactiveCocoa,根據(jù)文檔添加到工程。
ReactiveCocoa常用的類
1. RACSignal(信號)
RACSignal是RAC中最核心的類,如果把它學(xué)懂了, 就已經(jīng)可以用ReactiveCocoa進行開發(fā)了。
可以將RACSingnal當(dāng)做一個傳遞信號的工具,當(dāng)數(shù)據(jù)變化的時候,會將改變的信息發(fā)送出去,訂閱者接受到這個信息,執(zhí)行相應(yīng)的方法。
信號我們將它分為兩種:一種是冷信號,也是默認(rèn)的信號,這種信號,即使數(shù)據(jù)改變了,也不會發(fā)送信息;當(dāng)有訂閱者訂閱了這個信號,它才會變成熱信號,當(dāng)數(shù)據(jù)變化,它會將改變的數(shù)據(jù)發(fā)送出去,具體的,我們來看看RACSignal是怎么使用的:
// 1 創(chuàng)建一個信號
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3 發(fā)送數(shù)據(jù)
[subscriber sendNext:@"第一個信號"];
return [RACDisposable disposableWithBlock:^{
NSLog(@"取消信號");
}];
}];
/*********** 到這里還是“冷信號” ***********/
// 2 訂閱信號(冷信號變熱信號)
[signal subscribeNext:^(id x) {
// 4 處理信號
NSLog(@"%@", x);
}];
這里面寫了RACSignal基本的操作,信號的創(chuàng)建,訂閱以及發(fā)送。
2. RACSubscriber
上面的代碼中我們還看到了兩個類,在創(chuàng)建信號時block返回的類型 RACDisposable和參數(shù) RACSubscriber。
RACSubscriber:表示訂閱者的意思,在通過creat創(chuàng)建信號的時候,都會有一個訂閱者,來發(fā)送數(shù)據(jù)。
3. RACDisposable
RACDisposable:是用來取消訂閱、清理資源的。
這里我們還有一種寫法,來手動取消訂閱,這種情況,我們需要先來實例化個對象再來調(diào)用dispose方法:
// 訂閱信號
RACDisposable *disposable = [signal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
// 取消訂閱
[disposable dispose];
4. RACSubject & RACReplaySubject
RACSubject 是信號提供者,它既可以充當(dāng)信號,又可以發(fā)送信號
// 1創(chuàng)建信號
RACSubject *subject = [RACSubject subject];
// 2訂閱信號
[subject subscribeNext:^(id x) {
// 4處理信號
NSLog(@"訂閱:%@", x);
}];
// 3發(fā)送信號
[subject sendNext:@"信號"];
RACReplaySubject:是RACSubject的子類,重復(fù)提供信號。
// 創(chuàng)建信號
RACReplaySubject *replaySubject = [RACReplaySubject subject];
// 發(fā)送信號
[replaySubject sendNext:@"信號1"];
// 訂閱信號
[replaySubject subscribeNext:^(id x) {
NSLog(@"訂閱1:%@", x);
}];
// 發(fā)送信號
[replaySubject sendNext:@"信號2"];
// 訂閱信號
[replaySubject subscribeNext:^(id x) {
NSLog(@"訂閱2:%@", x);
}];
RACReplaySubject和RACSubject的最大的區(qū)別在于:
RACReplaySubject既可以先發(fā)送信號再訂閱,也可以先訂閱信號,再發(fā)送;而RACSubject只能先訂閱信號,再發(fā)送。
5.RACTuple(元組)
在Objective-C中,本身是沒有元組的,在Swift中有元組的概念。它與數(shù)組類似,可以以數(shù)組來初始化:
NSArray *arr = @[@"張三", @"10086", @"boy"];
RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:arr];
NSLog(@"\nname:%@\nid:%@\ngender:%@", [tuple objectAtIndex:0], [tuple objectAtIndex:1], [tuple objectAtIndex:2]);
ReactiveCocoa常用的方法
1. 監(jiān)聽事件
// 1. 監(jiān)聽事件
[[self.bt_Go rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"被點擊");
HomeViewController_2 *vc_2 = [[HomeViewController_2 alloc] initWithNibName:@"HomeViewController_2" bundle:nil];
vc_2.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:vc_2 animated:YES];
}];
2. 監(jiān)聽通知
// 2. 監(jiān)聽通知
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"socketMessage" object:nil] subscribeNext:^(id x) {
NSLog(@"收到!");
}];
3. 監(jiān)聽TextField的變化
// 3. 監(jiān)聽TextField變化
[self.tx_userName.rac_textSignal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
4. 監(jiān)聽變量屬性(KVO)
// 4. 監(jiān)聽變量屬性
self.user = [User new];
self.user.userId = 10086;
self.user.userName = @"張三";
[[self.user rac_valuesForKeyPath:@"userName" observer:nil] subscribeNext:^(id x) {
NSLog(@"用戶的名字改變了:%@", x);
}];
self.user.userName = @"李四";
5. 充當(dāng)代理
// 5. 充當(dāng)代理
self.headerView = [[[NSBundle mainBundle] loadNibNamed:@"HomeHeaderView" owner:self options:nil] lastObject];
self.headerView.frame = CGRectMake(0, SCREEN_HEIGHT - 300, SCREEN_WIDTH, 300);
[self.view addSubview:self.headerView];
[self.headerView.subject subscribeNext:^(User *user) {
NSLog(@"收到信號:\n用戶名:%@,用戶id:%ld", user.userName, user.userId);
}];
ReactiveCocoa常用的宏
1. RAC(TARGET, ...)
1.1 用途:
用來將某個對象的某個屬性和某個信號綁定。當(dāng)有信號過來,會自動賦值給對象的屬性。
1.2 用法:
// lb_name 是一個UILable, tx_userName是UITextField
RAC(self.lb_name, text) = self.tx_userName.rac_textSignal;
1.3 對比:
我們實現(xiàn)以下,在一個textfield中輸入內(nèi)容時,將它實時輸入到一個label中,看看以下幾種方法是怎么實現(xiàn)的,哪個更簡單。
- 不用RAC,通過給TextField添加方法來實現(xiàn)
- (void)viewDidLoad {
[super viewDidLoad];
[self.tx_userName addTarget:self action:@selector(showtextFiledContents) forControlEvents:UIControlEventEditingChanged];
}
- (void) showtextFiledContents{
self.lb_label1.text = self.tx_userName.text;
}
- 利用RAC綁定屬性和信號
[self.tx_userName.rac_textSignal subscribeNext:^(NSString *x) {
self.lb_label1.text = x;
}];
- 用RAC(TARGET, ...)實現(xiàn)
RAC(self.lb_label1, text) = self.tx_userName.rac_textSignal;
2. RACObserve(TARGET, KEYPATH)
2.1 用途:
監(jiān)聽某個對象的某個屬性,實際上它是[target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]
這句代碼的宏
2.2 用法
[RACObserve(self.user, userName) subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
2.3 對比
實現(xiàn)監(jiān)聽user的userName屬性
- 不使用 RACObserve,直接監(jiān)聽
self.user = [User new];
self.user.userId = 10086;
self.user.userName = @"張三";
[[self.user rac_valuesForKeyPath:@"userName" observer:nil] subscribeNext:^(id x) {
NSLog(@"用戶的名字改變了:%@", x);
}];
[[self.bt_Go rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
self.user.userName = @"李四";
}];
- 使用 RACObserve
self.user = [User new];
self.user.userId = 10086;
self.user.userName = @"張三";
[RACObserve(self.user, userName) subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
[[self.bt_Go rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
self.user.userName = @"李四";
}];
3. @weakify(Obj)和@strongify(Obj)
3.1用途:
防止循環(huán)引用
3.2 用法:
@weakify(Obj)和@strongify(Obj)是配合使用的,@weakify(Obj)寫在block外,@strongify(Obj)寫在block內(nèi)
@weakify(self)
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self)
[subscriber sendNext:self];
return nil;
}];
[signal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
4. RACTuplepack
用途:
把數(shù)據(jù)包裝成RACTuple(元組類)
用法:
RACTuple *tuple = RACTuplePack(@"123",@1);
5. RACTupleUnpack
用途:
把RACTuple(元組類)解包成對應(yīng)的數(shù)據(jù)
用法:
RACTupleUnpack(NSString *str,NSNumber *num) = tuple;
NSLog(@"%@ %@",str,num);