MVC-面向?qū)ο?/h1>
以上三者的交互關(guān)系。什么關(guān)系?
- Model 不與 View直接通話取视,如圖中Model和View之間的兩黃實線。
- Model 與 Controller 通話常挚,如圖中Model和Controller中兩灰色虛實線作谭、當(dāng)中的綠色箭頭、Model上橙色的KVO奄毡。
- View與 controller 通話折欠,如圖中Controller和View之間兩灰色虛實線、當(dāng)中的Outlet綠色箭頭吼过、黃色的delegate和data source锐秦。
- View上action 與 controller 上的target。
- Model上KVO發(fā)送端與controller上的黃色接受端盗忱。
MVC實際使用
目前App 中最常用的code習(xí)慣如下圖
通過這張圖可以發(fā)現(xiàn), 用戶信息頁面作為業(yè)務(wù)場景Scene需要展示多種數(shù)據(jù)M(Blog/Draft/UserInfo), 所以對應(yīng)的有多個View(blogTableView/draftTableView/image…), 但是, 每個MV之間并沒有一個連接層C, 本來應(yīng)該分散到各個C層處理的邏輯全部被打包丟到了Scene這一個地方處理, 也就是M-C-V變成了MM…-Scene-…VV, C層就這樣莫名其妙的消失了.
另外, 作為V的兩個cell直接耦合了M(blog/draft), 這意味著這兩個V的輸入被綁死到了相應(yīng)的M上, 復(fù)用無從談起.
MVC正常使用
那么正確的MVC是什么呢酱床?如下圖
MVC 的缺點
1、胖model的產(chǎn)生
2趟佃、Massive View Controller的產(chǎn)生
3扇谣、過度隔離V&M
4昧捷、產(chǎn)生太多輕量級的model
5、遺失網(wǎng)絡(luò)邏輯
6罐寨、較差的可測試性
7靡挥、業(yè)務(wù)邏輯與視圖邏輯強(qiáng)耦合
相關(guān)參考連接
http://www.reibang.com/p/7e5fc5fb7ba3
https://www.cnblogs.com/AnnieBabygn/p/7872552.html
MVP-面向接口
Model View Presenter
Controller中的所有邏輯都放在Presenter中實現(xiàn),與MVC不同的是Model與View是沒有任何聯(lián)系的衩茸,都是通過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-響應(yīng)式編程
MVVM其實是MVC的升級版芹血,controller & view 直接通過viewModel 讀取數(shù)據(jù)然后展示在界面上
MVVM概念
MVVM 簡單一點說 : 雙向綁定(通過viewModel 綁定view 與 model)
使用的嚴(yán)格限制
1、View 引用了viewModel 楞慈,但是反過來不行
2幔烛、viewModel引用了model,但是反過來不行
3囊蓝、MVVM模式依賴于數(shù)據(jù)綁定饿悬,它是一個框架級別的特性,用于自動連接對象屬性和UI控件聚霜。
viewModel 主要通過以下兩種方式通知View更新UI
1狡恬、Block
2、使用reactiveCocoa庫這個庫包含了函數(shù)式編程和響應(yīng)式編程蝎宇。其實reactiveCocoa庫就是MVVM模式的核心弟劲。
不使用 RAC :ViewModel處理完數(shù)據(jù)需要刷新 View等操作都是通過 Block回調(diào)來實現(xiàn),不免是有些麻煩的姥芥。
使用 RAC :在 View初始化的時候就和 ViewModel進(jìn)行綁定兔乞,只要 ViewModel的數(shù)據(jù)有所變化,便自動更新 View凉唐。
RAC 缺點:數(shù)據(jù)綁定使得一個位置的 Bug被快速傳遞到別的位置庸追,要定位原始出問題的地方就變得不那么容易了
ReactiveCocoa
reactiveCocoa 俗稱RAC,屬于函數(shù)式響應(yīng)編程台囱,極大的簡化了邏輯淡溯,方便使用
1、替換代理方法
2簿训、替換KVO
3咱娶、替換NSNotification
4、替換UIControl 的一些響應(yīng)事件
5强品、替換NSTimer
等等膘侮,采用管道式通信機(jī)制,使邏輯更易理解
ReactiveCocoa入門教程:第一部分
ReactiveCocoa入門教程: 第二部分
ReactiveCocoa源碼解析
ReactiveCocoa常用方法
MVVM+RAC 從框架到實戰(zhàn)
RAC使用
使用RAC模擬請求客戶檔案與倉庫檔案
1.先請求客戶檔案數(shù)量择懂,再根據(jù)數(shù)量分批次請求客戶檔案數(shù)據(jù)
2.先請求倉庫檔案數(shù)量喻喳,再根據(jù)數(shù)量分批次請求倉庫檔案數(shù)據(jù)
- (void)viewDidLoad
RACSignal *consumerSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
//模擬發(fā)送一次獲取客戶更新數(shù)量的請求
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(@"客戶檔案更新結(jié)束%@",x);
}];
RACSignal *warehouseSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
//模擬發(fā)送一次獲取倉庫更新數(shù)量的請求
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(@"倉庫檔案更新結(jié)束%@",x);
}];
//連接兩個信號,只有兩個信號都觸發(fā)時困曙,才能刷新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:@"客戶檔案請求成功1砺住Gァ!"];
}];
}
- (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:@"倉庫檔案請求成功1暮摺v蕖!"];
}]纲熏;
}
// 更新UI
- (void)updateUIWithR1:(id)data r3:(id)data2{