目錄
前言
RAC(ReactiveCocoa)
函數(shù)式編程彭沼、響應(yīng)式編程
Github上的開源框架
響應(yīng)式編程,是一種通用的編程范式,提高了開發(fā)效率脖旱。
我的理解就是:監(jiān)聽事件,然后在事件發(fā)生后做相應(yīng)回調(diào)處理币旧。
/**
delegate践险、通知、block
KVO
*/
在命令式編程環(huán)境中吹菱,a=b+c表示將表達(dá)式的結(jié)果賦給a巍虫,而之后改變b或c的值不會影響a。
但在響應(yīng)式編程中鳍刷,可以做到a的值隨b或c的更新而更新占遥。
1. 使用
引入(集成ReactiveCocoa框架)
pod 'ReactiveObjC'
#import <ReactiveObjC/ReactiveObjC.h>
- 常用
添加監(jiān)聽事件
// 給UITextFiled添加值改變事件
[[[UITextField new]rac_signalForControlEvents:UIControlEventEditingChanged]subscribeNext:^(id x) {
//
}];
等同于(簡化版)
[[[UITextField new]rac_textSignal]subscribeNext:^(NSString * _Nullable x) {
// UIControlEventEditingChanged
}];
// 給按鈕添加點擊事件
[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
添加手勢
[[UIButton new]addGestureRecognizer:({
UITapGestureRecognizer *tapG=[UITapGestureRecognizer new];
[[tapG rac_gestureSignal]subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
}];
tapG;
})];
調(diào)用方法
@weakify(self)
[[self rac_signalForSelector:@selector(viewDidLoad)]subscribeNext:^(id x) {
@strongify(self)
//
} error:^(NSError * _Nullable error) {
} completed:^{
}];
dele代理
UIAlertView *alertV=[[UIAlertView alloc]initWithTitle:@"alert" message:@"content" delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"sure", nil];
// 實現(xiàn)代理方法clickedButtonAtIndex 來自協(xié)議UIAlertViewDelegate
[[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)]subscribeNext:^(id _Nullable x) {
}];
// 等同于(簡化版)
[[alertV rac_buttonClickedSignal]subscribeNext:^(NSNumber * _Nullable x) {
}];
[alertV show];
NSNotificationCenter通知
添加觀察者
[[[NSNotificationCenter defaultCenter]rac_addObserverForName:@"notiName" object:nil]subscribeNext:^(NSNotification * _Nullable noti) {
// noti.name noti.object noti.object
}];
發(fā)送通知
[[NSNotificationCenter defaultCenter]postNotificationName:@"notiName" object:@[@"1",@"2"]];
KVO鍵值觀察
// 方式一
[RACObserve([UIScrollView new], contentOffset) subscribeNext:^(id _Nullable x) {
}];
// 方式二
[[self rac_valueForKeyPath:@"age" observer:nil] subscribeNext:^(id x ){
NSLog(@"%@",x);
}];
RACSubject
// 用于view中不同button的點擊事件
RACSubject *subj=[RACSubject subject];
[subj sendNext:@(100)];
// 外部調(diào)用
[subj subscribeNext:^(NSNumber *tag) {
}];
cell.clickSubject
@weakify(self);
[[cell.clickSubject takeUntil:cell.rac_prepareForReuseSignal] subscribeNext:^(NSNumber *x) {
@strongify(self);
[self clickCellBtn:[x intValue] atIndex:indexPath];
}];
值綁定
RAC(self.label.text) = _textField.rac_textSignal;
RAC(_personVM,mobile)=_phoneTF.rac_textSignal;
- 接口命令(用于MVVM)
+(RACSignal *)PostWithURL:(NSString *)urlStr parameters:(NSDictionary *)paramDic animated:(BOOL)animated{
return [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[YTNetworkTool POST:urlStr parameters:paramDic SuccessBlock:^(BOOL isOK, NSDictionary *dic) {
// 回調(diào)
[subscriber sendNext:dic];
[subscriber sendCompleted];
} failBlock:^(NSError *error) {
[subscriber sendError:error];
} animated:animated];
return [RACDisposable disposableWithBlock:^{
NSLog(@"信號發(fā)送完成或發(fā)送錯誤后調(diào)用,會自動執(zhí)行這個block,并取消訂閱信號");
}];
}]replayLazily];
}
_addRouteCommand=[[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) {
//
NSDictionary *paramDic=@{@"isOpen":self.isOpen};
return [YTServiceObject PostWithURL:API_AddRouteT parameters:paramDic animated:true];
}];
執(zhí)行命令(調(diào)用接口)
vm.isOpen=@(true);
[[vm.addRouteCommand execute:nil] subscribeNext:^(NSDictionary *dic) {
// dic
}];
[[[vm.addRouteCommand execute:nil]deliverOnMainThread] subscribeNext:^(NSDictionary *dic) {
// dic
}];
// 命令在執(zhí)行中(用于加載 加載中...)
[[vm.addRouteCommand executing]subscribeNext:^(NSNumber * _Nullable x) {
if(x.boolValue){
// 加載 加載中...
}else{
// 隱藏 加載中...
}
}];
兩個信號都收到時回調(diào)
RACSignal*signalA = [RACSignal createSignal:^RACDisposable *(id <RACSubscribe>subscriber){
NSLog(@"數(shù)據(jù)請求1");
[subscriber sendNext:@"數(shù)據(jù)請求1請求下來的數(shù)據(jù)"];
return nil;
}];
RACSignal*signalB = [RACSignal createSignal:^RACDisposable *(id <RACSubscribe>subscriber){
NSLog(@"數(shù)據(jù)請求2");
[subscriber sendNext:@"數(shù)據(jù)請求2請求下來的數(shù)據(jù)"];
return nil;
}];
[self rac_liftSelector:@Selector(updateUI) withSignalFromArray:@[signalA,signalB]];
//將 textfield 輸入信號的 返回值進(jìn)行修改 得到新的信號倾剿!
RACSignal *firstSignal = [self.firstTextfield.rac_textSignal map:^id(NSString *firstString) {
if (firstString.length >= 5 && firstString.length <= 10) {
return @(YES);
}
return @(NO);
}];
RACSignal *secondSignal = [self.secondTextfield.rac_textSignal map:^id(NSString *secondString) {
if (secondString.length >5 && secondString.length < 10) {
return @(YES);
}
return @(NO);
}];
// 綁定用戶名筷频、密碼判斷結(jié)果的2個信號量,如果都為真前痘,則按鈕可用
RAC(self.loginButton,enabled) = [RACSignal combineLatest:@[firstSignal,secondSignal] reduce:^(NSNumber *firstRes,NSNumber *secondRes){
return @(firstRes.boolValue && secondRes.boolValue);
}];
- 信號加條件
map 映射(攔截做額外處理)
[[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside]map:^id(id value) {
NSLog(@"value: %@",value);
return @"hello";
}]subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
filter 過濾(攔截不符合情況)
// 監(jiān)聽文本框的輸入,而且只有大于3個長度的時候才會打印
[[self.textField.rac_textSignal filter:^BOOL(id value) {
return [value length] > 3;
}]subscribeNext:^(id x) {
NSLog(@"x:%@",x);
}];
延時
[[RACScheduler mainThreadScheduler]afterDelay:2 schedule:^{
}];
[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]]subscribeNext:^(NSDate * date) {
}];
take:2 選取前兩個信號
skip:2 跳過前兩個信號
repeat 無限重復(fù)執(zhí)行
delay:3 延遲3s發(fā)送信號
throttle:0.5 0.5s內(nèi)信號不發(fā)生變化則觸發(fā)
distinctUntilChanged 不會連續(xù)發(fā)送兩次相同的信號
timeout:2 超時2s后觸發(fā)error
ignore:@"1" 忽略信號1
[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
[self.textField endEditing:YES];
// [alertView show];
// 創(chuàng)建信號
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"1"];
[subscriber sendNext:@"2"];
[subscriber sendNext:@"3"];
[subscriber sendNext:@"4"];
[subscriber sendCompleted];
return nil;
}] take:2]; // skip:2凛捏、repeat
// 發(fā)送信號
[signal subscribeNext:^(id x) {
NSLog(@"x : %@",x);
} completed:^{
NSLog(@"completed");
}];
}];
將多個不同類型的數(shù)據(jù)組合成一個元組
// 把參數(shù)中的數(shù)據(jù)包裝成元組
RACTuple *tuple = RACTuplePack(@"xmg",@20,@"m",@(999),@[@"a"],@{@"key":@"value"});
RACTupleUnpack(NSString *name,NSNumber *age,NSString *sex,NSNumber *price,NSArray *arr,NSDictionary *dic) = tuple;
NSLog(@"name:%@ age:%@ sex:%@ price:%@ arr:%@ dic:%@",name,age,sex,price,arr,dic);
遍歷數(shù)組、字典
NSArray *contentArr=@[@"hello",@"world",@"!"];
[contentArr.rac_sequence.signal subscribeNext:^(id _Nullable x) {
}];
NSDictionary *paramDic=@{@"hello":@"world"};
[paramDic.rac_sequence.signal subscribeNext:^(id _Nullable x) {
RACTwoTuple *tuple=(RACTwoTuple *)x;
// key: tuple[0]
// value: tuple[1]
}];
- 基礎(chǔ)
信號RACSignal
// 1. 創(chuàng)建信號
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3. 發(fā)送信號
[subscriber sendNext:@"唱歌"];
// 調(diào)用該方法銷毀信號芹缔,否則該信號一直占用著內(nèi)存
[subscriber sendCompleted];
// 3.1 發(fā)送error信號
[subscriber sendError:[NSError errorWithDomain:NSURLErrorDomain code:1001 userInfo:@{@"error":@"error message"}]];
// 4. 銷毀信號完畢后回調(diào)
return [RACDisposable disposableWithBlock:^{
NSLog(@"singal已銷毀");
}];
}];
// 2. 訂閱信號
RACDisposable *disposable = [signalA subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
// 取消訂閱
// [disposable dispose];
// 2.1 訂閱error信號
[signalA subscribeError:^(NSError * _Nullable error) {
NSLog(@"%@",error);
}];
信號動作:
信號映射:map坯癣、flattenMap
信號過濾:filter、ignore最欠、distinctUntilChanged
信號合并:combineLatest示罗、reduce、merge芝硬、zipWith
信號連接:concat蚜点、then
信號操作時間:timeout、interval拌阴、dely
信號跳過:skip
信號取值:take绍绘、takeLast、takeUntil
信號發(fā)送順序:donext、cocompleted
獲取信號中的信號:switchToLatest
信號錯誤重試:retry
避免循環(huán)引用
@weakify(self)
@strongify(self)