本篇內(nèi)容主要記錄一下平時(shí)項(xiàng)目中經(jīng)常會用到的rac的情況斤彼,不做太深入的研究匾鸥。
前言
ReactiveCocoa 可以說是結(jié)合了函數(shù)式編程和響應(yīng)式編程的框架敬拓,也可稱其為函數(shù)響應(yīng)式編程(FRP)框架菜皂,強(qiáng)調(diào)一點(diǎn)棒掠,RAC最大的優(yōu)點(diǎn)是提供了一個(gè)單一的、統(tǒng)一的方法去處理異步的行為立莉,包括delegate方法,blocks回調(diào),target-action機(jī)制,notifications和KVO.
一.導(dǎo)入
1.在項(xiàng)目的podfile文件中添加
# RAC
pod 'ReactiveObjC'
2.執(zhí)行pod install方法绢彤,即可導(dǎo)入框架。
3.在使用到rac的類中導(dǎo)入
//響應(yīng)式編程
#import <ReactiveObjC/ReactiveObjC.h>
我的項(xiàng)目中因?yàn)楹芏囝惗紩玫津殉埽灾苯釉趐ch文件導(dǎo)入
二.rac常規(guī)使用
1).button添加點(diǎn)擊事件
比如給一個(gè)登陸按鈕添加點(diǎn)擊事件
[[self.loginAccountBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
@strongify(self);
[self.navigationController pushViewController:[[NSClassFromString(@"GHLoginViewController") alloc] init] animated:YES];
}];
2).替代kvo監(jiān)聽
監(jiān)聽一些屬性的變化茫舶,只要屬性改變就會調(diào)用,并把改變的值傳遞給你刹淌。
//如:
@property(noatomic,assign) int age;
監(jiān)聽age的每一次賦值饶氏,并產(chǎn)生回調(diào)
[RACObserve(self, age) ]subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
監(jiān)聽age 的賦值,并忽略值為 “1” 的情況有勾,不執(zhí)行回調(diào)
[[RACObserve(self, networkStatus) ignore:@"1"]subscribeNext:^(id _Nullable x) {
}];
//模擬一個(gè)事件 觸摸屏幕 就讓age自增
-(void)touchesBegin:(NSSet<UITouch*>*)touches WithEvent:(UIEvent*)event{
age++;
}
//在任意類中監(jiān)聽networkStatus值的變化
@interface GHConfigDeviceManager : NSObject
@property (nonatomic) GHNetworkStatus networkStatus;
[[[RACObserve(GHConfigDeviceManager.share, networkStatus) skip:1] distinctUntilChanged] subscribeNext:^(id _Nullable x) {
NSLog(@"networkStatus----x=%@",x);
if ([x intValue] == 2) {
if (weakself.alertView) {
[weakself.alertView dismiss:nil];
}
}
}];
3).監(jiān)聽textfeild文字變化
實(shí)時(shí)監(jiān)測輸入文字變化
[[alertView.inputTextField rac_textSignal] subscribeNext:^(NSString * _Nullable x) {
//昵稱的長度范圍1~20個(gè)字符
[weakalertView.okBtn setTitleColor:(weakalertView.inputTextField.text.length == 0 || weakalertView.inputTextField.text.length > 20) ? ASColorHex(0xC1CCC9) : ASColorHex(0x0BD087) forState:UIControlStateNormal];
weakalertView.okBtn.enabled = (x.length == 0 || x.length > 20) ? NO : YES;
}];
4).監(jiān)聽通知回調(diào)
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIApplicationDidBecomeActiveNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
NSLog(@"x===%@",x);
}];
5).手勢執(zhí)行方法監(jiān)聽
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
self.lable.userInteractionEnabled = YES;
[self.lable addGestureRecognizer:tap];
[[tap rac_gestureSignal] subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
}];
6).數(shù)組與字典遍歷
數(shù)組遍歷
NSArray *array = @[@"111",@"222",@"333",@"444"];
[array.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"x11====%@",x);
}];
2021-05-27 11:22:41.734488+0800 GHome[5071:1101120] x11====222
2021-05-27 11:22:41.734570+0800 GHome[5071:1101120] x11====333
2021-05-27 11:22:41.734634+0800 GHome[5071:1101120] x11====444
字典遍歷
NSDictionary *dict = @{@"111":@"-111",@"222":@"-222",@"333":@"-333",@"444":@"-444"};
[dict.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"x22====%@",x);
}];
以下為打印內(nèi)容疹启,RACTwoTuple可視為二維數(shù)組類型
2021-05-27 11:19:24.804544+0800 GHome[5064:1099660] x22====<RACTwoTuple: 0x2834c09f0> (
222,
"-222"
)
2021-05-27 11:19:24.804825+0800 GHome[5064:1099660] x22====<RACTwoTuple: 0x2834c0bb0> (
111,
"-111"
)
2021-05-27 11:19:24.804941+0800 GHome[5064:1099660] x22====<RACTwoTuple: 0x2834c0bc0> (
444,
"-444"
)
2021-05-27 11:19:24.805051+0800 GHome[5064:1099660] x22====<RACTwoTuple: 0x2834c0be0> (
333,
"-333"
)
三 .常用RAC高階函數(shù)
1).信號合并combineLastest
RACSignal *accountSignal = self.accountTextField.rac_textSignal;
RACSignal *passwordSignal = self.passwordTextField.rac_textSignal;
RAC(self, loginBtn.enabled) = [RACSignal combineLatest:@[accountSignal, passwordSignal] reduce:^id(NSString *account, NSString *password){
@strongify(self);
BOOL b;
b = [account isValidatePhoneNumber];
return @(b && [password isValidatePassword]);
}];
account為accountSignal信號的回調(diào)值,password為passwordSignal信號的回調(diào)值蔼卡,我們在reduce回調(diào)函數(shù)里面處理?xiàng)l件判斷喊崖,并將判斷結(jié)果以信號的形式返回給loginBtn.enabled的值
2).map的使用
一句話概括map的作用:主要用于數(shù)據(jù)的再封裝與改造
例1 監(jiān)聽code_version的值變化,并字符串格式化雇逞,最后賦值到accessoryTitle屬性
RAC(self, accessoryTitle) = [RACObserve(self, deviceViewModel.code_version) map:^id _Nullable(NSString *_Nullable value) {
return ASStringFormat(@"V%@", value);
}];
例2 遍歷value數(shù)組并將value中元素重新組裝生成GHDeviceSettingModel值荤懂,然后重新生成數(shù)組array返回
[value.rac_sequence map:^GHDeviceSettingViewModel * _Nullable(NSDictionary *_Nullable value) {
@strongify(self);
GHDeviceSettingModel *model = [GHDeviceSettingModel mj_objectWithKeyValues:value];
return [[GHDeviceSettingViewModel alloc] initWithSettingModel:model deviceViewModel:self.deviceViewModel];
}].array
3). filter的使用
例1 只有在textfeild輸入框中的內(nèi)容少于六位的時(shí)候執(zhí)行回調(diào)
self.textField.rac_textSignal filter: BOOL (NSString *_ Nullable value) {
if (self.textField.text.length>6) f
self.textField.text = [self.textField.text substringToIndex:6];
}
return value.length<6;
}] subscribeNext:(NSString *_ Nullable x){
NSLog(@"filter----%@",x);
];
filter經(jīng)常情況伴隨著sequence(遍歷)一起使用
例2
遍歷數(shù)組dataSource的值,根據(jù)需求添加過濾條件喝峦,當(dāng)return YES時(shí)會添加到返回的數(shù)組array中势誊,return NO時(shí)不會添加到數(shù)組array中
NSArray *tempArray = [[self.OTAViewModel.dataSource rac_sequence] filter:^BOOL(GHOTACellViewModel* _Nullable value) {
if (value.type.intValue == 2) {
return YES;
}
return NO;
}].array;
使用rac進(jìn)行遍歷的好處就是,遍歷過程中不用重新創(chuàng)建Array或Dictionary谣蠢,即可拿到新生成的Array或Dictionary
4).flattenMap 映射
例 對輸入內(nèi)容進(jìn)行二次封裝處理
[[self.textField.rac_textSignal flattenMap:__kindof RACSignal *_ Nullable(NSString *
_Nullable value) {
NSLog( @"%@",value);//+8610086
return [RACReturnSignal return:[NSString stringWithFormat:@"+86%@",value]];
}] subscribeNext:^(id _Nullable х) {
NSLog(@"映射: %@",x);
}];
三.結(jié)合MVVM+RAC的簡單使用
這里簡單介紹一下mvvm
眾所周知MVC模式具有厚重的ViewController粟耻、遺失的網(wǎng)絡(luò)邏輯(沒有屬于它的位置)查近、較差的可測試性等問題。因此也就會有維護(hù)性較強(qiáng)挤忙、耦合性很低的一種新架構(gòu)MVVM (MVC 引申出得新的架構(gòu))的流行霜威。
主要在于 ViewModel
ViewModel: 相比較于MVC新引入的視圖模型。是視圖顯示邏輯册烈、驗(yàn)證邏輯戈泼、網(wǎng)絡(luò)請求等代碼存放的地方,唯一要注意的是赏僧,任何視圖本身的引用都不應(yīng)該放在VM中大猛,換句話說就是VM中不要引入U(xiǎn)IKit.h (對于image這個(gè),也有人將其看做數(shù)據(jù)來處理淀零,這就看個(gè)人想法了挽绩,并不影響整體的架構(gòu))。
比如一個(gè)用戶登錄網(wǎng)絡(luò)請求,將網(wǎng)絡(luò)請求相關(guān)邏輯都放到viewModel中執(zhí)行
@interface GHLoginViewModel : NSObject
@property (nonatomic, strong) RACCommand *loginCommand;
@property (nonatomic, strong) RACCommand *refreshTokenCommand;
@interface GHLoginRequest : GHNetworkBaseRequest
/// 手機(jī)號\郵箱
@property (nonatomic, copy) NSString *username;
/// 密碼(密碼由 8 - 128 字符組成驾中,不能為純數(shù)字或字母)
@property (nonatomic, copy) NSString *password;
/// 國家碼簡稱
@property (nonatomic, copy) NSString *region_code;
/// 手機(jī)號國家碼
@property (nonatomic, copy) NSNumber *phone_code;
@end
@implementation GHLoginViewModel
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
- (RACCommand *)loginCommand {
if (!_loginCommand) {
_loginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(GHLoginRequest* _Nullable input) {
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSString *taskId = [GHNetworkModule.share sendRequest:input cacheComplete:nil networkComplete:^(GHNetworkResponse *response) {
if (response.status == GHNetworkResponseStatusSuccess) {
GHUserInfo.share.password = input.password;
GHUserInfo.share.token = response.data[@"token"];
[subscriber sendNext:response.data];
[subscriber sendCompleted];
[GHUserInfo cacheInfo];
} else {
[subscriber sendError:response.error];
}
}];
return [RACDisposable disposableWithBlock:^{
[GHNetworkModule.share cancelRequestWithRequestID:taskId];
}];
}];
}];
}
return _loginCommand;
}
在viewcontoller中接收請求結(jié)果
//返回?cái)?shù)據(jù)處理
[self.viewModel.loginCommand.executionSignals.switchToLatest subscribeNext:^(id _Nullable x) {
@strongify(self)
[GHHudTip hideHUDWithView:self.view];
}];
//異常處理
[self.viewModel.loginCommand.errors subscribeNext:^(NSError * _Nullable x) {
@strongify(self)
[GHHudTip hideHUDWithView:self.view];
[GHHudTip showTips:x.domain];
}];
延伸與擴(kuò)展:iOS MVVM+RAC 從框架到實(shí)戰(zhàn)
結(jié)語:RAC的功能非常強(qiáng)大唉堪,且實(shí)用。這里只是列舉了一小部分肩民。其他還需要我們在開發(fā)中慢慢發(fā)掘唠亚。加油~