一篇关于RectiveCocoa的总结文档
百度搜索了一下RectiveCocoa,都是与MVVM关联在一起。
1·简述下MVVM
MVC:ModelViewViewController
MVVM:ModelViewView-Model
Model:容纳表现数据-模型对象信息的结构体,并在一个单独的管理类中维护的创建/管理模型的统一逻辑。
View:包含实际 UI 本身(不论是 UIView 代码, storyboard 和 xib), 任何视图特定的逻辑, 和对用户输入的反馈. 在 iOS 中这不仅需要 UIView 代码和那些文件, 还包括很多需由 UIViewController 处理的工作。
View-Model:
1、作为一个表现视图显示自身所需数据的静态模型;
2、收集, 解释和转换那些数据;
收集:网络请求,control控制的数值变更;
解释:赋值;
转换:viewmodel内的逻辑运算;
3、目的:让view(controller)的任务更清晰,就是展示view-Model提供的数据。
Summary:Model不变,view不变,增加View-Model分担ViewController的责任(每个view将有自己的view-model来约束)
其最终结果就是MVMCV
关于View-Model
不对视图控制器起作用,或通告其变化
关于ViewController
1、不再发起网络服务调用
2、不再管理数组(类似tableview的dataSource)
3、不再判断某些值是否有效
4、其实就是不再管text或者image是什么了
5、只管view-Model中的变化
Summary:将view-model与view关联,通过修改view model中的值的变更来修改view中的数值变化;ViewController就只能老老实实的管view-model的数值变更就好了。
2·ReactiveCocoa
解决View和View-Model关联的问题
举例
假设这是一个登陆页面,我要判断用户名是否合法,密码是否匹配,这些在MVC的情况下我都会在Controller中进行判断,是的话会怎么怎么样,不是的话会怎么怎么样。
而在MVVM和RectiveCocoa的帮助下,我们可以使用一个登陆的ViewModel,所有的判断和结果我可以写在ViewModel中。而在Controller中我只要1、关联用户名的text和viewmodel中的username,密码对应。2、就不用管TA了。
RectiveCocoa的学习
概念,一直是我不想去背却又不得不背的东西。
1、RACSignal:RAC的构造单元,代表我们最终收到的信息,(当你能将未来某事某刻接收到的消息具体表示出来,我们可以预先运用逻辑构建信息流,而不是等到事件发生)
简述:
1、信号是基本单位,也是写完代码会接收到的东西,因为是数据关联在view和view-model上,我们可以提前将需要展示出来的数据在viewmodel中处理了,再通过关联影响view的值的变化。而不是在controller中书写了。(个人理解,如有偏差,请各种指正)
2、信号会为了控制通过应用的信息流而获得所有这些异步方法(委托, 回调 block, 通知, KVO, target/action 事件观察, 等)并将它们统一到一个接口下.这只是直观理解. 不仅是这些, 因为信息会流过你的应用, 它还提供给你轻松转换/分解/合并/过滤信息的能力.
简述:中央集权制,信号中处理这些传值方式;并通过书写的逻辑达到预期的效果。
信号如何将ViewModel与View关联上
信号是一个发送一连串值的物体。需要在订阅者监听时信号才会发信息,信号会向订阅者发送0或多个带有数值的’next’事件,后面带有’complete’或’error’。
(信号类似于其他语言/工具包中的“promise”(1),但更强大,因为它不仅限于向它的订阅者一次只传递一个返回值. )
1、创建一个信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
return nil;
}];
2、信号中添加订阅者接收到的东西
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
//以下为新加
NSString *str=[NSString stringWithFormat:@"https://www.baidu.com"];
NSURL *url = [NSURL URLWithString:[str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]init];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[subscriber sendNext:@""];
[subscriber sendCompleted];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[subscriber sendError:error];
}];
[operation start];
//end
return nil;
}];
3、RACObserve()
这个宏是 RAC 中对 KVO 中那些悲惨的 API 的替代。 你只需要传入对象和你想观察的那个对象某属性的 keypath.。给出这些参数后, RACObserve 会创建一个信号,一旦它有了订阅者,,它就立刻发送那个属性的当前值,并在发送那个属性在这之后的任何变化。
RACSignal *signal1 = RACObserve(self. personModel,name);
//这行代码的前提是你有一个personModel,personModel有一个name属性
这仅是提供用于创建信号的一个工具. 这里有几个立即可用的方式, 来从内置控制流机制中拉取信号:
RACSignal *controlUpdate = [_myButtonrac_signalForControlEvents:UIControlEventTouchUpInside];
// signals for UIControl events send the control event value (UITextField, UIButton, UISlider, etc)
// subscribeNext:^(UIButton *button) { NSLog(@"%@", button); // UIButton instance }
RACSignal *textChange = [_myTextFieldrac_textSignal];
// some special methods are provided for commonly needed control event values off certain controls
// subscribeNext:^(UITextField *textfield) { NSLog(@"%@", _textfield.text); // "Hello!" }
RACSignal *alertButtonClicked = [_myAlertViewrac_buttonClickedSignal];
// signals for some delegate methods send the delegate params as the value
// e.g. UIAlertView, UIActionSheet, UIImagePickerControl, etc
// (limited to methods that return void)
// subscribeNext:^(NSNumber *buttonIndex) { NSLog(@"%@", buttonIndex); // "1" }
RACSignal *viewAppeared = [self rac_signalForSelector:@selector(viewDidAppear:)];
// signals for arbitrary selectors that return void, send the method params as the value
// works for built in or your own methods
// subscribeNext:^(NSNumber *animated) { NSLog(@"viewDidAppear %@", animated); // "viewDidAppear 1" }
什么是订阅者?
简言之,订阅者就是一段代码,它等待信号给它发送一些值,然后订阅者就能处理这些值了. (它也可以作用于“complete”和“error”事件. )
简述:理解为block,信号等同block调用?
RACSignal *usernameValidSignal = RACObserve(self. personModel,name);
// update the local property when this value changes
[usernameValidSignal subscribeNext: ^(NSNumber *isValidUserName) {
self.isValidName = isValidUserName.boolValue;
}];
以上代码前提:controller 有一个personModel,有一个BOOL类型的isValidName,Model中有Number类型的isValidUserName
那问题来了,isValidName是BOOL为何我要传NSNumber类型的。
答:RAC 只处理对象, 而不处理像 BOOL 这样的原始值.。不过不用担心,RAC 通常会帮你这些转换。
RAC(self,isValidName) = RACObserve(self.personModel, isValidUserName);
//前面是target 后面是target下的@property
//用中文讲,就是self.personModel的isValidUserName变化了,self.isValidName也要跟着变了。
那问题来了,self.isValidName做什么用,判断吗?那不就又绕回来了?把controller以前做判断啊什么的逻辑交给viewmodel就好了,所以呢……
这样我们可以把RACObserve(self.personModel, isValidUserName);绑定在textchange的协议方法中,或是确定按钮的enable中,等等等等所有用这个值判断的位置上(选位置时注意,不要总想在controller中操作)。
多个订阅者, 副作用, 昂贵的操作
订阅信号链时要明白重要的一件事是每当一个新值通过信号链被发送出去时, 实际上会给每个订阅者都发送一次. 直到意识到这就我们而言是有意义的, 信号发出的值不存储在任何地方(除了 RAC 在内部实现中). 当信号需要发送一个新的值时, 它会遍历所有的订阅者并给每个订阅者发送那个值. (这是对信号链实际工作的简化说明, 但基本想法是对的)
这为什么重要?这意味着信号链某处存在的任何副作用, 任何影响应用世界的转变, 将会发生多次. 这对新接触 RAC 的用户来说是意想不到的. (这也违反了函数式构建的理念-数据输入, 数据输出).
一个做作的例子可能是: 信号链某处的信号在每次按钮被按下时更新 self 中的一个计数器属性. 如果信号链有多个订阅者, 计数器的增长将会比你想的还要多. 你需要努力从信号链中尽可能剔除副作用. 当副作用不可避免时, 你可以使用一些恰当的预防机制. 我将会在另一篇文章中探索.
简述:当存在多个订阅者的时候,就像一对多广播一样,只要是订阅者就会都遍历一次。在设计之初就应该规范,不管是针对子viewModel的分别控制,还是条件限制,等等,
除副作用之外, 我们需要注意带有昂贵操作和可变数据的信号链. 网络请求就是一个三者兼得的例子:
网络请求影响了应用的网络层(副作用).
网络请求为信号链引入了可变数据. (两个完全一样请求可能返回了不同的数据. )
网络请求反应慢啊.
假设我根据一个text,text作为参数,发起多个网络请求,我所要的结果在网络请求的结果中,那么多个订阅者如何处理这个返回结果,下一篇介绍RACCommand
额外:自行百度-mock、method swizzling
(1)promise的概念
ES6原生提供了Promise对象。
所谓Promise,就是一个对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的API,可供进一步处理。