内容提要:

本文首先对比MVC简单介绍了MVVM的概念和优点,其次,简单介绍了Reactive Cocoa的使用,最后,通过一个例子介绍了使用Reactive Cocoa的MVVM框架。

正文:

  首先推荐2篇MVVM介绍的特别好的文章:《ReactiveCocoa 和 MVVM 入门》《MVVM Tutorial with ReativeCocoa

  查了一些资料,特别是看完这两篇长文章后,又自己做了实验,把自己认为的一些比较好的观点和理解,记录在自己的文章中,主要是供自己复习。

1. MVVM框架

(1)什么是MVVM?

  MVVM,即 M-V-VM (Model-View-ViewModel)

  先从下图的MVC和MVVM的对比主观理解一下。

   如下图MVC和MVVM的对比图,MVVM的整个框架从MVC的角度可以按下图理解:MVC的C部分可以分成2部分,一部分给了MVVM的VM(View-Model)部分,一部分给了MVVM的V(View)部分。(对比图中的第一行和第二行理解)

另外,MVVM的V(View)部分,一部分是View Controller部分,一部分是View部分,也就是说整个MVVM的本质结构M+V+VM即 Model+View Controller、View+View Model。


(图片地址:https://github.com/SheronLv/Images/blob/master/MVVM/MVVM与MVC对比图.jpg)

(2)为什么提出MVVM框架?

  MVC的缺点:ios Architecture, where MVC stands for Massive View Controller.

  MVVM的优点:This pattern, facilitated by Reactive Cocoa, provides an excellent alternative to MVC, and guarantees sleek and lightweight view controllers. 即MVVM是为了解决MVC中View Controller部分过于庞大,把MVC的Controller部分变的轻量级。

  除了上面view部分变的轻量级,View-Model部分对“应用世界”的依赖和影响也尽可能减少,View-Model部分的单元测试变的可行,是MVVM的另一个优点。

2. Reactive Cocoa

  ReactiveCocoa is vital for binding the View and ViewModel together

特点:Reactive Cocoa 将我们常见的异步信息流控制工具(比如:Delegate、Block callbacks、Target Action、KVO、Notifications)统一到RACSignal下。

   Reactive Cocoa can searve as a replacement for the delegate pattern, target-action pattern, key-value observing, and more.

优点:Reactive Cocoa使用了更高层次的抽象。

概念:

(1)信号类RACSiganl:一些等待某事发生的代码,然后把结果值发送给他们的订阅者;

(2)订阅者:是一段代码,它等待信号给它发送一些值,然后订阅者就能处理这些值了(如何订阅信号:调用信号RACSignal的subscribeNext就能订阅);

(3)RACCommand:代表UI action,它包含一个signal和当前状态。这个signal是UI action的结果,状态表明action是否被执行。

举几个例子来学习一下Reactive Cocoa的语法,具体的学习可以查阅其他资料:

//@property (strong, nonatomic) RACCommand *executeSearch;

//(1)map使用
RACSignal *validSearchSignal = [[RACObserve(self, searchText)
//RACObserve创建一个信号Signal,this is a ReactiveCocoa wrapper over KVO(self.searchText是NSString类型)
map:^id(NSString *text) { // map操作,将text 转换成true和false的信号流
               return @(text.length > );
                }]
                 distinctUntilChanged]; //distinctUntilChanged保证,当state改变时,信号才被发出 //(2)subscribeNext使用
[validSearchSignal subscribeNext:^(id x) { NSLog(@"search text is valid %@", x); }];//signal的信号放出来就是x的值
//(3)RACCommand使用
self.executeSearch = [[RACCommand alloc] initWithEnabled:validSearchSignal
signalBlock:^RACSignal *(id input) { return [self executeSearchSignal]; //此函数返回一个RACSignal }]; self.searchButton.rac_command = self.viewModel.executeSearch; //rac_command是UIButton的一个Reactive Cocoa属性
点击searchButton之后变成不可点击状态,需[self executeSearchSignal]函数执行完,返回执行完的状态,searchButton才又可点击。
//(4)combineLatest、reduce使用
// 绑定提交按钮可用状态的视图逻辑 

RACSignal *requestingSingal = [RACObserve(self.viewModel, requestingQuesDetailInfo) distinctUntilChanged];

[[[RACSignal combineLatest:@[requestQuesDetailInfoSuccessSignal, quesStateSignal]
reduce:^id(NSNumber *requestQuesDetailInfoSuccess, NSString *quesState){
BOOL hidden = (![requestQuesDetailInfoSuccess boolValue]
                                          || [quesState isEqualToString:@"已解决"]);  
                                             return @(hidden);
                              }] distinctUntilChanged]
subscribeNext:^(NSNumber *hidden) {
@strongify(self); self.commentTextView.hidden = [hidden boolValue];
}];

另外,RAC代替代理、KVO、监听事件、代替通知、监听文本变化等常用方法,举例如下:

(参考:http://www.jianshu.com/p/87ef6720a096)

// 1.代替代理
//把调用某个对象的方法的信息转换成信号
[[VC rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) {
NSLog(@"点击按钮");
}]; // 2.KVO
// 把监听redV的center属性改变转换成信号
[[redV rac_valuesAndChangesForKeyPath:@"center" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x)
NSLog(@"%@",x);
}]; // 3.监听事件
// 把按钮点击事件转换为信号,点击按钮,就会发送信号
[[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) { NSLog(@"按钮被点击了");
}]; // 4.代替通知
// 把监听到的通知转换信号
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {
NSLog(@"键盘弹出");
}]; // 5.监听文本框的文字改变
[_textField.rac_textSignal subscribeNext:^(id x) { NSLog(@"文字改变了%@",x);
}]; // 6.处理多个请求,都返回结果的时候,统一做处理.
RACSignal *request1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { // 发送请求1
[subscriber sendNext:@"发送请求1"];
return nil;
}]; RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 发送请求2
[subscriber sendNext:@"发送请求2"];
return nil;
}]; // 使用注意:几个信号,参数一的方法就几个参数,每个参数对应信号发出的数据。
[self rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalsFromArray:@[request1,request2]]; }
// 更新UI
- (void)updateUIWithR1:(id)data r2:(id)data1
{
NSLog(@"更新UI%@ %@",data,data1);
}

3.举例

新建XYViewModel继承自NSObject,关键代码如下,此部分是MVVM的viewModel部分:

//暴露的请求接口标志属性如下
@property (nonatomic, assign) BOOL loading;
@property (nonatomic, assign) BOOL loadingSuccess;
@property (nonatomic, strong) NSString * errorMsg;
//请求数据
- (void)requestData
{
self.loading = YES;
self.loadingSuccess = NO;
@weakify(self);
[[.... cachePolicy:NVCacheTypeDisabled] subscribeNext:^(XYmodel * model) {
@strongify(self);
self.loading=NO;
self.loadingSuccess=YES;
} error:^(NSError * error) {
@strongify(self);
self.loading=NO;
self.loadingSuccess=NO;
self.errorMsg=[error nv_message];
}];
}

ViewController里的关键代码如下,主要是绑定viewModel的信号部分:

- (void)viewDidLoad
{ self.viewModel = [[XYViewModel alloc] init];
[self bindViewModel];
[self refreshData];
} - (void)bindViewModel
{
//提示网络错误
@weakify(self);
[RACObserve(self.viewModel, errorMsg) subscribeNext:^(NSString * errorMsg) {
@strongify(self);
[self setShowLoading:NO];
if(errorMsg&&errorMsg.length>){
[self showSplash:errorMsg];
}
[self.tableView reloadData];
[self refreshFinished];
}]; //请求结束
[RACObserve(self.viewModel, loading) subscribeNext:^(NSNumber * chaLoading) {
@strongify(self);
if ([chaLoading boolValue]==NO) {
[self setShowLoading:NO];
}
}]; //请求成功
[RACObserve(self.viewModel, loadingSuccess) subscribeNext:^(NSNumber * loadingSuccess) {
@strongify(self);
if ([loadingSuccess boolValue] == YES) {
[self setShowLoading:NO];
[self.tableView reloadData];
[self refreshFinished];
}
}];
}

这样就把ViewModel和ViewController绑定了起来。

另外,ViewModel还需要暴露View要展示的数据供View使用。

MVVM with ReactiveCocoa的更多相关文章

  1. MVVM With ReactiveCocoa让移动开发更简单

    作者:@雷纯锋2011 MVVM是一种软件架构模式,它是 Martin Fowler 的 Presentation Model 的一种变体,最先由微软的架构师 John Gossman 在 2005 ...

  2. [干货分享]一篇可能会让你爱上MVVM与ReactiveCocoa的文章

    概要 在此工程中,本文将讨论将MVC改造为MVVM需要的一些基本方法,同时会适当穿插部分关于MVVM概念性的讨论!本文最大的意义在于,提供了一种读者可以复现的方式,逐步引出从MVC向MVVM尽可能平滑 ...

  3. ReactiveCocoa 和 MVVM 入门 (转)

    翻译自ReactiveCocoa and MVVM, an Introduction. 文中引用的 Gist 可能无法显示.为了和谐社会, 请科学上网. MVC 任何一个正经开发过一阵子软件的人都熟悉 ...

  4. 一次MVVM+ReactiveCocoa实践

    前言 学习MVVM和ReactiveCocoa(简称RAC)也有一段时间了,不过都仅限于看博客,一直对这两个东西很感兴趣,觉得很创新,也一直想找个机会在项目中实践一下,但是还是有一些顾虑,毕竟没有实践 ...

  5. 【长篇高能】ReactiveCocoa 和 MVVM 入门

    翻译自ReactiveCocoa and MVVM, an Introduction. 文中引用的 Gist 可能无法显示.为了和谐社会, 请科学上网. MVC 任何一个正经开发过一阵子软件的人都熟悉 ...

  6. 被误解的MVC和被神化的MVVM(转)

    转载自:http://www.infoq.com/cn/articles/rethinking-mvc-mvvm 原文作者:唐巧 被误解的 MVC MVC 的历史 MVC,全称是 Model View ...

  7. IOS:被误解的MVC和被神化的MVVM

    MVC的历史 MVC,全称是 Model View Controller,是模型 (model)-视图 (view)-控制器 (controller) 的缩写.它表示的是一种常见的客户端软件开发框架. ...

  8. ReactiveCocoa 入门学习 (一)

    引言 现在由于需求的不断发展,MVC这个经典的框架由于Controller的任务越来越多,显得"臃肿"了,网上又推出了新的框架,比如MVVM,ReactiveCocoa, 今天就来 ...

  9. 【转】伟大的RAC和MVVM入门(一)

    原文:http://www.sprynthesis.com/2014/12/06/reactivecocoa-mvvm-introduction/   翻译自ReactiveCocoa and MVV ...

随机推荐

  1. 超级楼梯[HDU2041]

    超级楼梯 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...

  2. BZOJ1077 : [SCOI2008]天平

    首先通过差分约束系统建图,用Floyed算法求出任意两个砝码差值的上下界. 然后暴力枚举放在右边的砝码C,D,通过与A,B差值的上下界分类讨论统计方案. 时间复杂度$O(N^3)$. #include ...

  3. phonegap+html5开发app的一些总结

    1.Css3圆角白边:使用css3圆角效果时,在android某些机器上会产生白边,所以应该在圆角的div外套一个div(背景色和外部相同),然后有圆角效果的div 内部使用自己的背景色 border ...

  4. 转:Web前端,高性能优化

    高性能HTML 一.避免使用iframe iframe也叫内联frame,可将一个HTML文档嵌入另一个HTML文档中. iframe的好处是,嵌入的文档独立于父文档,通常也借此使浏览器模拟多线程.缺 ...

  5. 【BZOJ】2329: [HNOI2011]括号修复(splay+特殊的技巧)

    http://www.lydsy.com/JudgeOnline/problem.php?id=2329 和前一题一样,不就多了个replace操作吗.好,就打一下. 然后交上去wa了........ ...

  6. POJ 3280 Cheapest Palindrome(DP)

    题目链接 被以前的题目惯性思维了,此题dp[i][j],代表i到j这一段变成回文的最小花费.我觉得挺难的理解的. #include <cstdio> #include <cstrin ...

  7. 原创Java版的Shell

    如果你接触过windows操作系统,你应该对windows中的cmd有一定的了解. 如果你接触过Linux操作系统,你应该对Linux的shell有一定的了解. 本文说的正是linux中的shell. ...

  8. 设置TOMCAT的JVM虚拟机内存大小

    你知道如何设置TOMCAT的JVM虚拟机内存大小吗,这里和大家分享一下,JAVA程序启动时JVM都会分配一个初始内存和最大内存给这个应用程序.这个初始内存和最大内存在一定程度都会影响程序的性能. 设置 ...

  9. [转载]CString类常用方法----Left(),Mid(),Right()……

    CStringLeft(intnCount)const; //从左边1开始获取前 nCount个字符 CStringMid(intnFirst)const; //从左边第 nCount+1个字符开始, ...

  10. OpenStack手动从数据库中删除实例 - ugyn109的专栏 - 博客频道 - CSDN.NET

    由于某种原因我将OpenStack的一个计算节点移除了,但移除前并没有删除在其上运行的实例,后来想通过dash删除这些实例,于是N天过去了,我的dash还显示如下内容:很碍眼是不是?于是我打算手动从数 ...