MVC-面向对象
以上三者的交互关系。什么关系?
- Model 不与 View直接通话,如图中Model和View之间的两黄实线。
- Model 与 Controller 通话,如图中Model和Controller中两灰色虚实线、当中的绿色箭头、Model上橙色的KVO。
- View与 controller 通话,如图中Controller和View之间两灰色虚实线、当中的Outlet绿色箭头、黄色的delegate和data source。
- View上action 与 controller 上的target。
- Model上KVO发送端与controller上的黄色接受端。
MVC实际使用
目前App 中最常用的code习惯如下图
通过这张图可以发现, 用户信息页面作为业务场景Scene需要展示多种数据M(Blog/Draft/UserInfo), 所以对应的有多个View(blogTableView/draftTableView/image…), 但是, 每个MV之间并没有一个连接层C, 本来应该分散到各个C层处理的逻辑全部被打包丢到了Scene这一个地方处理, 也就是M-C-V变成了MM…-Scene-…VV, C层就这样莫名其妙的消失了.
另外, 作为V的两个cell直接耦合了M(blog/draft), 这意味着这两个V的输入被绑死到了相应的M上, 复用无从谈起.
MVC正常使用
那么正确的MVC是什么呢?如下图
MVC 的缺点
1、胖model的产生
2、Massive View Controller的产生
3、过度隔离V&M
4、产生太多轻量级的model
5、遗失网络逻辑
6、较差的可测试性
7、业务逻辑与视图逻辑强耦合
相关参考连接
https://www.jianshu.com/p/7e5fc5fb7ba3
https://www.cnblogs.com/AnnieBabygn/p/7872552.html
MVP-面向接口
Model View Presenter
Controller中的所有逻辑都放在Presenter中实现,与MVC不同的是Model与View是没有任何联系的,都是通过Presenter来交互
Presenter持有MVPView与MVPModel
#import "MVPViewController.h"
#import "Presenter.h"
#import "MVPModel.h"
#import "MVPView.h"
@interface MVPViewController ()
@property (nonatomic, strong) Presenter *presenter;
@property (nonatomic, strong) MVPView *mvpView;
@property (nonatomic, strong) MVPModel *mvpModel;
@end
@implementation MVPViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_presenter = [[Presenter alloc] init];
_mvpView = [[MVPView alloc] init];
_mvpView.frame = self.view.bounds;
[self.view addSubview:_mvpView];
_mvpModel = [MVPModel new];
_mvpModel.content = @"MVP的模式";
// model还没赋值
_presenter.model = _mvpModel;
_presenter.view = _mvpView;
[_presenter usageLogic];
}
@end
MVVM-响应式编程
MVVM其实是MVC的升级版,controller & view 直接通过viewModel 读取数据然后展示在界面上
MVVM概念
MVVM 简单一点说 : 双向绑定(通过viewModel 绑定view 与 model)
使用的严格限制
1、View 引用了viewModel ,但是反过来不行
2、viewModel引用了model,但是反过来不行
3、MVVM模式依赖于数据绑定,它是一个框架级别的特性,用于自动连接对象属性和UI控件。
viewModel 主要通过以下两种方式通知View更新UI
1、Block
2、使用reactiveCocoa库这个库包含了函数式编程和响应式编程。其实reactiveCocoa库就是MVVM模式的核心。
不使用 RAC :ViewModel处理完数据需要刷新 View等操作都是通过 Block回调来实现,不免是有些麻烦的。
使用 RAC :在 View初始化的时候就和 ViewModel进行绑定,只要 ViewModel的数据有所变化,便自动更新 View。
RAC 缺点:数据绑定使得一个位置的 Bug被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了
ReactiveCocoa
reactiveCocoa 俗称RAC,属于函数式响应编程,极大的简化了逻辑,方便使用
1、替换代理方法
2、替换KVO
3、替换NSNotification
4、替换UIControl 的一些响应事件
5、替换NSTimer
等等,采用管道式通信机制,使逻辑更易理解
ReactiveCocoa入门教程:第一部分
ReactiveCocoa入门教程: 第二部分
ReactiveCocoa源码解析
ReactiveCocoa常用方法
MVVM+RAC 从框架到实战
RAC使用
使用RAC模拟请求客户档案与仓库档案
1.先请求客户档案数量,再根据数量分批次请求客户档案数据
2.先请求仓库档案数量,再根据数量分批次请求仓库档案数据
- (void)viewDidLoad
RACSignal *consumerSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
//模拟发送一次获取客户更新数量的请求
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//假如获取到count 5
[self requestConsumerDataWith:subscriber];
});
return nil;
}];
[consumerSignal subscribeNext:^(id _Nullable x) {
g
NSLog(@"客户档案更新结束%@",x);
}];
RACSignal *warehouseSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
//模拟发送一次获取仓库更新数量的请求
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//假如获取到count 5
[self requestConsumerDataWith:subscriber];
});
return nil;
}];
[warehouseSignal subscribeNext:^(id _Nullable x) {
NSLog(@"仓库档案更新结束%@",x);
}];
//连接两个信号,只有两个信号都触发时,才能刷新UI 或者让HUD 消失
[self rac_liftSelector:@selector(updateUIWithR1:r3:) withSignalsFromArray:@[warehouseSignal,consumerSignal]];
}
- (void)requestConsumerDataWith:(id<RACSubscriber>)subScriber_consumer{
RACSignal *consumerSignal = nil;
for (int i = 1; i < 5; i++) {
RACSignal *single_temp = [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subscriber sendNext:@(i)];
});
return nil;
}]delay:i];
if (!consumerSignal) {
consumerSignal = single_temp;
}else{
consumerSignal = [consumerSignal combineLatestWith:single_temp];
}
}
[consumerSignal subscribeNext:^(id _Nullable x) {
[subScriber_consumer sendNext:@"客户档案请求成功!!!"];
}];
}
- (void)requestWarehouseDataWith:(id<RACSubscriber>)subScriber_warehouse{
RACSignal *consumerSignal = nil;
for (int i = 1; i < 5; i++) {
RACSignal *single_temp = [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subscriber sendNext:@(i)];
});
return nil;
}]delay:i];
if (!consumerSignal) {
consumerSignal = single_temp;
}else{
consumerSignal = [consumerSignal combineLatestWith:single_temp];
}
}
[consumerSignal subscribeNext:^(id _Nullable x) {
[subScriber_warehouse sendNext:@"仓库档案请求成功!!!"];
}];
}
// 更新UI
- (void)updateUIWithR1:(id)data r3:(id)data2{