ReactiveCocoa是iOS開(kāi)發(fā)的一個(gè)開(kāi)源第三方框架杨何,又被稱(chēng)作函數(shù)響應(yīng)是編程框架净赴。它結(jié)合了函數(shù)式編程和響應(yīng)式編程兩種編程風(fēng)格俺亮,定義了一個(gè)標(biāo)準(zhǔn)的接口來(lái)響應(yīng)諸如點(diǎn)擊按鈕驮捍,網(wǎng)絡(luò)請(qǐng)求完成等事件。
函數(shù)式編程&響應(yīng)式編程
- 何謂函數(shù)式編程
函數(shù)式編程就是把操作任務(wù)盡量寫(xiě)成一系列嵌套的函數(shù)或方法來(lái)調(diào)用脚曾。
這種編程思想有幾個(gè)特點(diǎn):方法的返回值是他本身东且,參數(shù)是Block,如果對(duì)block不太熟悉的同學(xué),可以先看一下這篇文章:iOS Block本讥,做一下了解苇倡。
- 何謂響應(yīng)式編程
我們可以用KVO來(lái)理解響應(yīng)式編程。一個(gè)類(lèi)中有一個(gè)user對(duì)象囤踩,user有“userId”“userName”兩個(gè)屬性旨椒,我們利用KVO來(lái)觀察user的userName屬性,當(dāng)userName屬性變化了的時(shí)候堵漱,會(huì)執(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的方式有兩種:
- 通過(guò)CocoaPods添加
#RAC
pod 'ReactiveCocoa', '~> 2.5'
- 通過(guò)GitHub手動(dòng)下載ReactiveCocoa勤庐,根據(jù)文檔添加到工程示惊。
ReactiveCocoa常用的類(lèi)
1. RACSignal(信號(hào))
RACSignal是RAC中最核心的類(lèi),如果把它學(xué)懂了愉镰, 就已經(jīng)可以用ReactiveCocoa進(jìn)行開(kāi)發(fā)了米罚。
可以將RACSingnal當(dāng)做一個(gè)傳遞信號(hào)的工具,當(dāng)數(shù)據(jù)變化的時(shí)候丈探,會(huì)將改變的信息發(fā)送出去录择,訂閱者接受到這個(gè)信息,執(zhí)行相應(yīng)的方法碗降。
信號(hào)我們將它分為兩種:一種是冷信號(hào)隘竭,也是默認(rèn)的信號(hào),這種信號(hào)讼渊,即使數(shù)據(jù)改變了动看,也不會(huì)發(fā)送信息;當(dāng)有訂閱者訂閱了這個(gè)信號(hào)爪幻,它才會(huì)變成熱信號(hào)菱皆,當(dāng)數(shù)據(jù)變化须误,它會(huì)將改變的數(shù)據(jù)發(fā)送出去,具體的,我們來(lái)看看RACSignal是怎么使用的:
// 1 創(chuàng)建一個(gè)信號(hào)
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3 發(fā)送數(shù)據(jù)
[subscriber sendNext:@"第一個(gè)信號(hào)"];
return [RACDisposable disposableWithBlock:^{
NSLog(@"取消信號(hào)");
}];
}];
/*********** 到這里還是“冷信號(hào)” ***********/
// 2 訂閱信號(hào)(冷信號(hào)變熱信號(hào))
[signal subscribeNext:^(id x) {
// 4 處理信號(hào)
NSLog(@"%@", x);
}];
這里面寫(xiě)了RACSignal基本的操作仇轻,信號(hào)的創(chuàng)建京痢,訂閱以及發(fā)送。
2. RACSubscriber
上面的代碼中我們還看到了兩個(gè)類(lèi)拯田,在創(chuàng)建信號(hào)時(shí)block返回的類(lèi)型 RACDisposable和參數(shù) RACSubscriber历造。
RACSubscriber:表示訂閱者的意思甩十,在通過(guò)creat創(chuàng)建信號(hào)的時(shí)候船庇,都會(huì)有一個(gè)訂閱者,來(lái)發(fā)送數(shù)據(jù)侣监。
3. RACDisposable
RACDisposable:是用來(lái)取消訂閱鸭轮、清理資源的。
這里我們還有一種寫(xiě)法橄霉,來(lái)手動(dòng)取消訂閱窃爷,這種情況,我們需要先來(lái)實(shí)例化個(gè)對(duì)象再來(lái)調(diào)用dispose方法:
// 訂閱信號(hào)
RACDisposable *disposable = [signal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
// 取消訂閱
[disposable dispose];
4. RACSubject & RACReplaySubject
RACSubject 是信號(hào)提供者姓蜂,它既可以充當(dāng)信號(hào)按厘,又可以發(fā)送信號(hào)
// 1創(chuàng)建信號(hào)
RACSubject *subject = [RACSubject subject];
// 2訂閱信號(hào)
[subject subscribeNext:^(id x) {
// 4處理信號(hào)
NSLog(@"訂閱:%@", x);
}];
// 3發(fā)送信號(hào)
[subject sendNext:@"信號(hào)"];
RACReplaySubject:是RACSubject的子類(lèi),重復(fù)提供信號(hào)钱慢。
// 創(chuàng)建信號(hào)
RACReplaySubject *replaySubject = [RACReplaySubject subject];
// 發(fā)送信號(hào)
[replaySubject sendNext:@"信號(hào)1"];
// 訂閱信號(hào)
[replaySubject subscribeNext:^(id x) {
NSLog(@"訂閱1:%@", x);
}];
// 發(fā)送信號(hào)
[replaySubject sendNext:@"信號(hào)2"];
// 訂閱信號(hào)
[replaySubject subscribeNext:^(id x) {
NSLog(@"訂閱2:%@", x);
}];
RACReplaySubject和RACSubject的最大的區(qū)別在于:
RACReplaySubject既可以先發(fā)送信號(hào)再訂閱逮京,也可以先訂閱信號(hào),再發(fā)送束莫;而RACSubject只能先訂閱信號(hào)懒棉,再發(fā)送。
5.RACTuple(元組)
在Objective-C中览绿,本身是沒(méi)有元組的策严,在Swift中有元組的概念。它與數(shù)組類(lèi)似饿敲,可以以數(shù)組來(lái)初始化:
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)聽(tīng)事件
// 1. 監(jiān)聽(tīng)事件
[[self.bt_Go rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"被點(diǎn)擊");
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)聽(tīng)通知
// 2. 監(jiān)聽(tīng)通知
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"socketMessage" object:nil] subscribeNext:^(id x) {
NSLog(@"收到!");
}];
3. 監(jiān)聽(tīng)TextField的變化
// 3. 監(jiān)聽(tīng)TextField變化
[self.tx_userName.rac_textSignal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
4. 監(jiān)聽(tīng)變量屬性(KVO)
// 4. 監(jiān)聽(tīng)變量屬性
self.user = [User new];
self.user.userId = 10086;
self.user.userName = @"張三";
[[self.user rac_valuesForKeyPath:@"userName" observer:nil] subscribeNext:^(id x) {
NSLog(@"用戶(hù)的名字改變了:%@", 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(@"收到信號(hào):\n用戶(hù)名:%@妻导,用戶(hù)id:%ld", user.userName, user.userId);
}];
ReactiveCocoa常用的宏
1. RAC(TARGET, ...)
1.1 用途:
用來(lái)將某個(gè)對(duì)象的某個(gè)屬性和某個(gè)信號(hào)綁定。當(dāng)有信號(hào)過(guò)來(lái)怀各,會(huì)自動(dòng)賦值給對(duì)象的屬性栗竖。
1.2 用法:
// lb_name 是一個(gè)UILable, tx_userName是UITextField
RAC(self.lb_name, text) = self.tx_userName.rac_textSignal;
1.3 對(duì)比:
我們實(shí)現(xiàn)以下渠啤,在一個(gè)textfield中輸入內(nèi)容時(shí)狐肢,將它實(shí)時(shí)輸入到一個(gè)label中,看看以下幾種方法是怎么實(shí)現(xiàn)的沥曹,哪個(gè)更簡(jiǎn)單份名。
- 不用RAC碟联,通過(guò)給TextField添加方法來(lái)實(shí)現(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綁定屬性和信號(hào)
[self.tx_userName.rac_textSignal subscribeNext:^(NSString *x) {
self.lb_label1.text = x;
}];
- 用RAC(TARGET, ...)實(shí)現(xiàn)
RAC(self.lb_label1, text) = self.tx_userName.rac_textSignal;
2. RACObserve(TARGET, KEYPATH)
2.1 用途:
監(jiān)聽(tīng)某個(gè)對(duì)象的某個(gè)屬性,實(shí)際上它是[target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]
這句代碼的宏
2.2 用法
[RACObserve(self.user, userName) subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
2.3 對(duì)比
實(shí)現(xiàn)監(jiān)聽(tīng)user的userName屬性
- 不使用 RACObserve僵腺,直接監(jiān)聽(tīng)
self.user = [User new];
self.user.userId = 10086;
self.user.userName = @"張三";
[[self.user rac_valuesForKeyPath:@"userName" observer:nil] subscribeNext:^(id x) {
NSLog(@"用戶(hù)的名字改變了:%@", 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)寫(xiě)在block外,@strongify(Obj)寫(xiě)在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(元組類(lèi))
用法:
RACTuple *tuple = RACTuplePack(@"123",@1);
5. RACTupleUnpack
用途:
把RACTuple(元組類(lèi))解包成對(duì)應(yīng)的數(shù)據(jù)
用法:
RACTupleUnpack(NSString *str,NSNumber *num) = tuple;
NSLog(@"%@ %@",str,num);