目錄
- MVC的簡介
- MVC三者的職責(zé)和關(guān)系
- 標(biāo)準(zhǔn)MVC與非標(biāo)準(zhǔn)MVC的區(qū)別及利弊
- 項(xiàng)目實(shí)戰(zhàn)應(yīng)用(標(biāo)準(zhǔn)MVC寫法與非標(biāo)準(zhǔn)MVC寫法對比)
- view上面的用戶行為事件如何處理惭蟋?
- 如何更新對應(yīng)的數(shù)據(jù)模型鹦肿?
- 數(shù)據(jù)模型更新了后如何處理?
- 如何更新 view 上面的視圖元素?
簡介
摘自百度百科對MVC的解釋 MVC
MVC (全名是Model View Controller玫镐,是模型(model)-視圖(view)-控制器(controller)的縮寫倒戏,一種軟件設(shè)計(jì)典范,用一種業(yè)務(wù)邏輯恐似、數(shù)據(jù)杜跷、界面顯示分離的方法組織代碼,將業(yè)務(wù)邏輯聚集到一個(gè)部件里面矫夷,在改進(jìn)和個(gè)性化定制界面及用戶交互的同時(shí)葛闷,不需要重新編寫業(yè)務(wù)邏輯。MVC被獨(dú)特的發(fā)展起來用于映射傳統(tǒng)的輸入双藕、處理和輸出功能在一個(gè)邏輯的圖形化用戶界面的結(jié)構(gòu)中淑趾。
三者的職責(zé)
Model(模型)
是應(yīng)用程序中用于處理應(yīng)用程序數(shù)據(jù)邏輯的部分,通常模型對象負(fù)責(zé)在數(shù)據(jù)庫中存取數(shù)據(jù)忧陪。View(視圖)
是應(yīng)用程序中處理數(shù)據(jù)顯示的部分扣泊,通常視圖是依據(jù)模型數(shù)據(jù)創(chuàng)建的近范。Controller(控制器)
是應(yīng)用程序中處理用戶交互的部分,通逞有罚控制器負(fù)責(zé)從視圖讀取數(shù)據(jù)评矩,控制用戶輸入,并向模型發(fā)送數(shù)據(jù)等孵。
或許很多小伙伴感覺很疑惑稚照,下面我們就結(jié)合例子詳細(xì)說說MVC
一 初識(shí) MVC
我們先看一副圖蹂空,很清晰明了的描述了三者之間的關(guān)系
三者職責(zé)解釋說明
Model層
數(shù)據(jù)處理層俯萌,包括網(wǎng)絡(luò)請求,數(shù)據(jù)加工View層
所有App上看得到的界面Controller層
Model 與 View層的中介上枕,把Model數(shù)據(jù)在View上展示出來
對應(yīng)箭頭解釋
-
view
將用戶交互通知給controller
咐熙,通常使用代理。 -
controller
通過更新model
來反應(yīng)狀態(tài)的改變辨萍。 -
model
(通常使用KVO
)通知controller
來更新他們負(fù)責(zé)的view
二 變異的MVC
網(wǎng)上還有另外一幅圖也很形象的表現(xiàn)iOS實(shí)際開發(fā)中MVC架構(gòu)圖
這張圖是iOS的MVC架構(gòu)中最經(jīng)常出現(xiàn)的圖棋恼,因?yàn)镮OS中的Controlller 是UIViewController
,所以導(dǎo)致很多人會(huì)把視圖寫在Controller中锈玉,這樣無疑會(huì)導(dǎo)致VC很臃腫爪飘。
因此,M-VC
可能是對 iOS 開發(fā)中的 MVC模式更為準(zhǔn)確的解讀拉背,同時(shí)更也準(zhǔn)確地描述了我們?nèi)粘i_發(fā)可能已經(jīng)編寫的 MVC 代碼师崎,但它并沒有做太多事情來解決 iOS 應(yīng)用中日益增長的重量級(jí)視圖控制器的問題。
三 MVC的利與弊
在 iOS 開發(fā)中椅棺,MVC(Model View Controller)是構(gòu)建iOS App的標(biāo)準(zhǔn)模式犁罩,是蘋果推薦的一個(gè)用來組織代碼的權(quán)威范式。
Apple甚至是這么說的两疚。在MVC下床估,所有的對象被歸類為一個(gè)Model,一個(gè)View诱渤,和一個(gè)Controller丐巫。Model持有數(shù)據(jù),View顯示與用戶交互的界面勺美,而ViewController調(diào)解Model和View之間的交互〉蓦剩現(xiàn)在,MVC 依然是目前主流客戶端編程框架励烦,但同時(shí)它也被調(diào)侃成Massive View Controller(重量級(jí)視圖控制器)谓着,想必開發(fā)者在開發(fā)中無可避免被下面幾個(gè)問題所困擾:
- 厚重的ViewController
- 遺失的網(wǎng)絡(luò)邏輯(無立足之地)
- 較差的可測試性
接下來就讓我們一起探討MVC的弊端,剖析問題產(chǎn)生原因坛掠,打造一個(gè)輕量級(jí)的ViewController赊锚,明確MVC設(shè)計(jì)模式中各個(gè)角色的職責(zé)治筒。
3.1 厚重的View Controller
Model:
模型model的對象通常非常的簡單。根據(jù)Apple的文檔舷蒲,model應(yīng)包括數(shù)據(jù)
和操作數(shù)據(jù)的業(yè)務(wù)邏輯
耸袜。而在實(shí)踐中,model層往往非常薄牲平,不管怎樣堤框,model層的業(yè)務(wù)邏輯不應(yīng)被拖入到controller。
View:
視圖view通常是UIKit控件纵柿,View不應(yīng)該直接引用model(PS:現(xiàn)實(shí)中蜈抓,使用了),并且僅僅通過IBAction事件引用controller昂儒。業(yè)務(wù)邏輯很明顯不歸入view沟使,視圖本身沒有任何業(yè)務(wù)。
Controller:
Controller是app的膠水代碼
:協(xié)調(diào)模型和視圖之間的所有交互渊跋±拔耍控制器負(fù)責(zé)管理他們所擁有的視圖的視圖層次結(jié)構(gòu),還要響應(yīng)視圖的loading拾酝、appearing燕少、disappearing等等,同時(shí)往往也會(huì)充滿我們不愿暴露的model的模型邏輯以及不愿暴露給視圖的業(yè)務(wù)邏輯蒿囤。
網(wǎng)絡(luò)數(shù)據(jù)的請求及后續(xù)處理客们,本地?cái)?shù)據(jù)庫操作,以及一些帶有工具性質(zhì)輔助方法都加大了Massive View Controller
的產(chǎn)生蟋软。
3.2 遺失(無處安放)的網(wǎng)絡(luò)邏輯
蘋果使用的MVC的定義是這么說的:所有的對象都可以被歸類為一個(gè)model
镶摘,一個(gè)view
,或是一個(gè)controller
岳守。
你可能試著把它放在Model
對象里凄敢,但是也會(huì)很棘手,因?yàn)榫W(wǎng)絡(luò)調(diào)用應(yīng)該使用異步
湿痢,這樣如果一個(gè)網(wǎng)絡(luò)請求比持有它的model生命周期更長涝缝,事情將變的復(fù)雜。顯然View里面做網(wǎng)絡(luò)請求那就更格格不入了譬重,因此只剩下Controller了拒逮。若這樣,這又加劇了Massive View Controller
的問題臀规。若不這樣滩援,何處才是網(wǎng)絡(luò)邏輯的家呢?
3.3 較差的可測試性
由于View Controller混合了視圖處理邏輯和業(yè)務(wù)邏輯塔嬉,分離這些成分的單元測試成了一個(gè)艱巨的任務(wù)玩徊。
四 項(xiàng)目實(shí)戰(zhàn)
4.1 實(shí)際開發(fā)中MVC的使用
我們仿頭條主頁樣式寫了一個(gè)測試用例租悄,然后來講解實(shí)際開發(fā)的用法。
核心類講解
- NewsModel 新聞模型數(shù)據(jù)類
@interface NewsModel : NSObject
/** id */
@property(nonatomic, copy)NSString *newsId;
/** icon */
@property(nonatomic, copy)NSString *icon;
/** title */
@property(nonatomic, copy)NSString *title;
/** subTitle */
@property(nonatomic, copy)NSString *subTitle;
/** content */
@property(nonatomic, copy)NSString *content;
/** if attention */
@property(nonatomic, assign, getter=isAttention)BOOL attention;
/** imgList */
@property(nonatomic, copy)NSArray *imgs;
/** share number */
@property(nonatomic, assign)NSUInteger shareNum;
/** discuss num */
@property(nonatomic, assign)NSUInteger discussNum;
/** like */
@property(nonatomic, assign)NSUInteger likeNum;
/** if like */
@property(nonatomic, assign,getter=isLike)BOOL like;
@end
- NewsCell.h 新聞視圖類
@class NewsModel;
@interface NewsCell : UITableViewCell
/** model */
@property(nonatomic, strong)NewsModel *model;
@end
- NewsCell.m 實(shí)現(xiàn)類
// 1.我們使用懶加載的形式加載視圖
/** icon */
@property(nonatomic, strong)UIImageView *iconImgView;
/** title */
@property(nonatomic, strong)UILabel *titleLbe;
/** subTitle */
@property(nonatomic, strong)UILabel *subTitleLbe;
...... // 粘貼部分代碼
// 2.采用masonry約束布局
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.contentView.backgroundColor = [UIColor whiteColor];
[self drawUI];
}
return self;
}
#pragma mark - drawUI
- (void)drawUI {
[self.contentView addSubview:self.iconImgView];
[self.contentView addSubview:self.titleLbe];
[self.contentView addSubview:self.subTitleLbe];
[self.iconImgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeMake(44, 44));
make.top.equalTo(self.contentView.mas_top).offset(10);
make.leading.equalTo(self.contentView.mas_leading).offset(10);
}];
[self.titleLbe mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.equalTo(self.iconImgView.mas_trailing).offset(10);
make.bottom.equalTo(self.iconImgView.mas_centerY).offset(-2);
}];
[self.subTitleLbe mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.equalTo(self.titleLbe.mas_leading);
make.top.equalTo(self.iconImgView.mas_centerY).offset(2);
}];
...... // 粘貼部分代碼
}
// 3.設(shè)置數(shù)據(jù)
#pragma mark - set
- (void)setModel:(NewsModel *)model {
_model = model;
[self.iconImgView sd_setImageWithURL:[NSURL URLWithString:model.icon]];
self.titleLbe.text = model.title;
[self.titleLbe sizeToFit];
self.subTitleLbe.text = model.subTitle;
[self.subTitleLbe sizeToFit];
...... // 粘貼部分代碼
}
// 4.懶加載形式加載控件
#pragma mark - lazy
- (UIImageView *)iconImgView {
if (_iconImgView == nil) {
_iconImgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
_iconImgView.layer.cornerRadius = 22;
_iconImgView.layer.masksToBounds = YES;
}
return _iconImgView;
}
- (UILabel *)titleLbe {
if (_titleLbe == nil) {
_titleLbe = [self getLbeWithFont:16 textColor:[UIColor blackColor]];
}
return _titleLbe;
}
- (UILabel *)subTitleLbe {
if (_subTitleLbe == nil) {
_subTitleLbe = [self getLbeWithFont:14 textColor:[UIColor grayColor]];
}
return _subTitleLbe;
}
- ViewController 控制器
// tableView也使用懶加載的形式
- (UITableView *)tableView {
if (_tableView == nil) {
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, kScreenWidth, kScreenHeight - 64) style:UITableViewStyleGrouped];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.scrollsToTop = true;
_tableView.backgroundColor = [UIColor whiteColor];;
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_tableView.showsVerticalScrollIndicator = NO;
_tableView.scrollsToTop = YES;
_tableView.estimatedRowHeight = 250;//預(yù)估高度
_tableView.rowHeight = UITableViewAutomaticDimension;
[_tableView registerClass:[NewsCell class] forCellReuseIdentifier:cellId];
__weak typeof(self) weakSelf = self;
[_tableView addPullToRefreshWithActionHandler:^{
[weakSelf refreshData];
}];
[_tableView addInfiniteScrollingWithActionHandler:^{
[weakSelf loadNextPage];
}];
}
return _tableView;
}
// 核心代碼
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.dataSource.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NewsModel *model = [self.dataSource objectAtIndex:indexPath.row];
NewsCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.model = model;
return cell;
}
注解說明
- 一個(gè)基本的數(shù)據(jù)展示就完成了恩袱,相信很多開發(fā)小伙伴也是這樣做的泣棋,如果沒有涉及到用戶的交互行為,純粹是做展示畔塔,那基本就完工了潭辈。但是實(shí)際開發(fā)中往往不是這樣的。
- 比如用戶點(diǎn)擊了
分享
澈吨,評(píng)論
把敢,點(diǎn)贊
,關(guān)注
棚辽,刪除
等按鈕技竟,那我們應(yīng)該如何處理數(shù)據(jù)及更新頁面呢?
接下來我們就從以下幾點(diǎn)出發(fā)來闡述標(biāo)準(zhǔn)的MVC
和非標(biāo)準(zhǔn)的MVC
兩者之間的區(qū)別屈藐。
4.1 view上面的用戶行為事件如何處理?
當(dāng)用戶在視圖上做了點(diǎn)擊熙尉,那我們應(yīng)該如何處理用戶的點(diǎn)擊事件联逻?以本文實(shí)例中點(diǎn)贊
為例子說明。
說明:當(dāng)用戶點(diǎn)擊贊或者取消贊检痰,我們需要告知后臺(tái)用戶的行為包归,同時(shí)更新對應(yīng)的視圖。
4.1.1 標(biāo)準(zhǔn)MVC寫法
view
將user action
傳給VC
铅歼,一共有三種方式可以將view
上的行為傳遞給VC
公壤,分別是代理
,block
和通知
椎椰。本文介紹如果使用代理
將用戶的行為告知VC厦幅。
- NewsCell.h (聲明一個(gè)cell的協(xié)議,并定義一些方法)
@protocol NewsCellDelegate <NSObject>
// tap like
- (void)didTapNewsCellLike:(NewsModel *)newsModel;
@end
@interface NewsCell : UITableViewCell
/** delegate */
@property(nonatomic,weak)id<NewsCellDelegate> delegate;
@end
- NewsCell.m (在點(diǎn)擊回調(diào)方法中調(diào)用該協(xié)議方法)
// 用戶點(diǎn)擊了點(diǎn)贊按鈕
- (void)tapLike {
if ([self.delegate respondsToSelector:@selector(didTapNewsCellLike:)]) {
[self.delegate didTapNewsCellLike:self.model];
}
}
- ViewController.m (設(shè)置代理并實(shí)現(xiàn)代理方法即可)
@interface ViewController ()<UITableViewDataSource, UITableViewDelegate, NewsCellDelegate>
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NewsModel *model = [self.dataSource objectAtIndex:indexPath.row];
NewsCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.model = model;
cell.delegate = self; // VC作為Cell視圖的代理對象
return cell;
}
- (void)didTapNewsCellLike:(NewsModel *)newsModel {
// 處理view上的點(diǎn)擊事件
}
4.1.2 非標(biāo)準(zhǔn)的MVC寫法
有些小伙伴或許想直接在view視圖中處理網(wǎng)絡(luò)慨飘,數(shù)據(jù)确憨,然后更新對應(yīng)視圖,和VC沒有關(guān)系瓤的。
實(shí)例代碼如下
- NewsCell.m (直接在視圖中調(diào)用網(wǎng)絡(luò)請求并處理數(shù)據(jù))
// 用戶點(diǎn)擊了點(diǎn)贊按鈕
- (void)tapLike {
// 直接發(fā)起網(wǎng)絡(luò)請求并處理回調(diào)事件
__weak typeof(self) weakSelf = self;
[self.model addLike:^(NSDictionary *json) {
weakSelf.model.like = !weakSelf.model.isLike;
if (weakSelf.model.isLike) {
[weakSelf.likeActionView updateImgName:@"like_red"];
weakSelf.model.likeNum++;
} else {
[weakSelf.likeActionView updateImgName:@"like"];
weakSelf.model.likeNum--;
}
[weakSelf.likeActionView updateTitle:[NSString stringWithFormat:@"%lu",(unsigned long)weakSelf.model.likeNum]];
}];
}
運(yùn)行結(jié)果
如果在用戶不拖拽的情況下休弃,該方法是可以實(shí)現(xiàn)效果的,但是一旦用戶進(jìn)行了拖拽圈膏,我們知道UITableView是采用
重用機(jī)制
塔猾,所以對應(yīng)的視圖和模型數(shù)據(jù)都會(huì)發(fā)生變化,我們可以將請求前和請求后的對象地址打印一下就知道了稽坤。
2019-04-14 10:04:16.496819+0800 MVC-Demo[65856:2082156] old model 0x6000011d9680
2019-04-14 10:04:18.745787+0800 MVC-Demo[65856:2082156] new model 0x6000011d9860
由打印結(jié)果可知丈甸,模型對象發(fā)生了變化医增,所以此種方法不可取。
第二種方法
或許有的小伙伴想老虫,在view中完成網(wǎng)絡(luò)請求叶骨,然后將結(jié)果告知VC,然后由VC來更新對應(yīng)的數(shù)據(jù)及視圖祈匙,此種方法可以忽刽,但是不推薦這樣做,因?yàn)閁ITableViewCell重用機(jī)制的原因夺欲。
4.2 如何更新對應(yīng)的數(shù)據(jù)模型跪帝?
4.2.1 標(biāo)準(zhǔn)的MVC寫法
根據(jù)蘋果官方的推薦,模型包含數(shù)據(jù)處理層些阅,包括網(wǎng)絡(luò)請求伞剑,數(shù)據(jù)加工及處理。
實(shí)例代碼如下
- NewsModel.m (新聞模型對象市埋,里面封裝了點(diǎn)贊的網(wǎng)絡(luò)請求及數(shù)據(jù)處理)
/// 添加點(diǎn)贊
- (void)addLike:(void(^)(NSDictionary *json))callback {
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
NSURLSessionTask *task = [session dataTaskWithURL:[NSURL URLWithString:API_GetGaoShiList] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
self.like = !self.like;
if (self.like) {
self.likeNum += 1;
} else {
self.likeNum -= 1;
}
}
if (callback) {
callback(nil);
}
});
}];
[task resume];
}
- viewController.m (外界直接調(diào)用黎泣,監(jiān)聽回調(diào)block即可)
- (void)didTapNewsCellLike:(NewsModel *)newsModel {
__weak typeof(NewsModel *) weakNewsModel = newsModel;
[newsModel addLike:^(NSDictionary *json) {
// 更新對應(yīng)的視圖
[self updateNewsView:weakNewsModel];
}];
}
運(yùn)行結(jié)果
我們將點(diǎn)贊的網(wǎng)絡(luò)請求處理,數(shù)據(jù)處理都封裝到了模型里面缤谎,外界直接調(diào)用并監(jiān)聽結(jié)果的回調(diào)即可抒倚。
4.2.2 非標(biāo)準(zhǔn)的MVC寫法
有些小伙伴喜歡將網(wǎng)絡(luò)請求之間寫在VC里面,然后在VC里面處理請求和數(shù)據(jù)的處理坷澡。
實(shí)例代碼
- ViewController.m (之間在VC中發(fā)網(wǎng)絡(luò)請求托呕,然后通過newId更新對應(yīng)的數(shù)據(jù)模型)
- (void)didTapNewsCellLike:(NewsModel *)newsModel {
// 非標(biāo)準(zhǔn)的MVC寫法
[self postLikeNetwork:newsModel];
}
#pragma mark - like network + data dealwith
- (void)postLikeNetwork:(NewsModel *)newsModel {
NSString *api = @"http://rap2api.taobao.org/app/mock/163155/gaoshilist"; // 告示
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
NSURLSessionTask *task = [session dataTaskWithURL:[NSURL URLWithString:api] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
[self dealwithLikeData:newsModel.newsId];
}
});
}];
[task resume];
}
- (void)dealwithLikeData:(NSString *)newsId {
__block NewsModel *newsModel;
[self.dataSource enumerateObjectsUsingBlock:^(NewsModel *obj, NSUInteger idx, BOOL *stop) {
if ([obj.newsId isEqualToString:newsId]) {
newsModel = obj;
*stop = YES;
}
}];
if (newsModel) {
newsModel.like = !newsModel.like;
if (newsModel.like) {
newsModel.likeNum += 1;
} else {
newsModel.likeNum -= 1;
}
[self updateNewsView:newsModel];
}
}
運(yùn)行結(jié)果
這種方法也可以實(shí)現(xiàn)功能,而且不會(huì)出問題频敛,但是很明顯项郊,代碼行數(shù)增加了,并且會(huì)增加VC的臃腫斟赚,所以不是很推薦着降。
4.3 數(shù)據(jù)模型更新了后如何處理?
一般數(shù)據(jù)模型更新了后都需要更新對應(yīng)的視圖汁展,那是直接去更新視圖操作還是先通知VC鹊碍,然后讓VC去更新對應(yīng)的視圖。
4.3.1 標(biāo)準(zhǔn)的MVC寫法
蘋果官方推薦當(dāng)模型數(shù)據(jù)更新后告知VC食绿,方法主要有三種侈咕,分別是delegate
,block
和通知器紧。根據(jù)使用場景耀销,三種方法各有優(yōu)缺點(diǎn),本文以block為例講解铲汪。
使用場景:用戶更新點(diǎn)贊狀態(tài)后熊尉,需要告知后臺(tái)罐柳,然后更新對應(yīng)的模型數(shù)據(jù),然后在更新視圖狰住。
- NewsModel.m (處理網(wǎng)絡(luò)請求张吉,更新數(shù)據(jù)然后回調(diào)給VC)
/// 添加點(diǎn)贊
- (void)addLike:(void(^)(NSDictionary *json))callback {
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
NSURLSessionTask *task = [session dataTaskWithURL:[NSURL URLWithString:API_GetGaoShiList] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
self.like = !self.like;
if (self.like) {
self.likeNum += 1;
} else {
self.likeNum -= 1;
}
}
if (callback) {
callback(nil);
}
});
}];
[task resume];
}
viewController.m (在回調(diào)中更新視圖)
- (void)didTapNewsCellLike:(NewsModel *)newsModel {
// 標(biāo)準(zhǔn)的MVC寫法
__weak typeof(NewsModel *) weakNewsModel = newsModel;
[newsModel addLike:^(NSDictionary *json) {
// 更新對應(yīng)的視圖
[self updateNewsView:weakNewsModel];
}];
}
標(biāo)準(zhǔn)的MVC寫法是在模型對象內(nèi)部完成數(shù)據(jù)的處理,然后再告知VC催植。
4.3.1 非標(biāo)準(zhǔn)的MVC寫法
有些小伙伴喜歡在view視圖中直接對模型進(jìn)行操作肮蛹,數(shù)據(jù)的處理等操作。例子和之前的類似
- NewsCell.m(當(dāng)用戶點(diǎn)贊后创南,直接在視圖中發(fā)起網(wǎng)絡(luò)請求伦忠,處理數(shù)據(jù),然后發(fā)通知更新對應(yīng)的視圖)
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.contentView.backgroundColor = [UIColor whiteColor];
[self drawUI];
[self addNotify]; // 監(jiān)聽通知
}
return self;
}
#pragma mark - notify
- (void)addNotify {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onNotifyModelUpdate:) name:kNotifyModelUpdate object:nil];
}
- (void)onNotifyModelUpdate:(NSNotification *)notify {
NewsModel *model = (NewsModel *)notify.object;
if (model == nil) {
return;
}
if (![self.model.newsId isEqualToString:model.newsId]) {
return;
}
// 更新視圖操作
if (self.model.isLike) {
[self.likeActionView updateImgName:@"like_red"];
} else {
[self.likeActionView updateImgName:@"like"];
}
[self.likeActionView updateTitle:[NSString stringWithFormat:@"%lu",(unsigned long)self.model.likeNum]];
}
// 用戶點(diǎn)擊了點(diǎn)贊按鈕
- (void)tapLike {
/**
* 數(shù)據(jù)模型更新了后如何處理稿辙?
* 直接發(fā)通知,然后視圖監(jiān)聽通知并刷新視圖
*/
__weak typeof(self) weakSelf = self;
[self.model addLike:^(NSDictionary *json) {
// 發(fā)通知
[[NSNotificationCenter defaultCenter] postNotificationName:kNotifyModelUpdate object:weakSelf.model];
}];
}
運(yùn)行結(jié)果
結(jié)果運(yùn)行正常昆码,沒有任何問題,這種寫法就只是
view
和model
之間進(jìn)行交互邻储,沒有涉及到vc
赋咽,雖然可以實(shí)現(xiàn)功能,但是違背了MVC的設(shè)計(jì)初衷芥备,在視圖中做了很多不該視圖做的事情冬耿,而且代碼也比較多,復(fù)雜萌壳。
4.4 如何更新 view 上面的視圖元素
當(dāng)用戶點(diǎn)贊之后或者取消點(diǎn)贊視圖,需要將對應(yīng)的視圖圖標(biāo)替換日月,那當(dāng)數(shù)據(jù)更新后袱瓮,我們?nèi)绾胃耉iew上面的視圖元素呢?
4.4.1 標(biāo)準(zhǔn)的MVC寫法
由VC來更新對應(yīng)的視圖
- viewController.m (在VC中更新視圖)
- (void)updateNewsView:(NewsModel *)newsModel {
__block NSUInteger index = NSNotFound;
[self.dataSource enumerateObjectsUsingBlock:^(NewsModel *obj, NSUInteger idx, BOOL *stop) {
if ([newsModel.newsId isEqualToString:obj.newsId]) {
index = idx;
*stop = YES;
}
}];
if (index == NSNotFound) {
return;
}
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
通過
newsId
爱咬,找到該模型在數(shù)據(jù)源中的索引尺借,然后tableView更新對應(yīng)位置的``cell`即可。
4.4.1 非標(biāo)準(zhǔn)的MVC寫法
視圖中監(jiān)聽對應(yīng)通知精拟,然后更新對應(yīng)的視圖燎斩,前面例子已經(jīng)提到過了
- NewsCell.m (直接在視圖中監(jiān)聽模型數(shù)據(jù)的變化,然后更新對應(yīng)視圖的元素)
#pragma mark - notify
- (void)addNotify {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onNotifyModelUpdate:) name:kNotifyModelUpdate object:nil];
}
- (void)onNotifyModelUpdate:(NSNotification *)notify {
NewsModel *model = (NewsModel *)notify.object;
if (model == nil) {
return;
}
if (![self.model.newsId isEqualToString:model.newsId]) {
return;
}
// 更新視圖操作
if (self.model.isLike) {
[self.likeActionView updateImgName:@"like_red"];
} else {
[self.likeActionView updateImgName:@"like"];
}
[self.likeActionView updateTitle:[NSString stringWithFormat:@"%lu",(unsigned long)self.model.likeNum]];
}
1.給
cell
添加監(jiān)聽通知蜂绎,當(dāng)模型數(shù)據(jù)發(fā)生變化后栅表,全局發(fā)通知,每一個(gè)cell
監(jiān)聽到通知后都需要去做判斷师枣,即當(dāng)前cell
對應(yīng)的model
是否是需要更新的model
怪瓶,通過newsId
來區(qū)分。
2.很明顯践美,這種方法顯得比較low
洗贰,而且每一個(gè)cell
都要添加監(jiān)聽找岖,然后做判斷,性能較低敛滋。
本文參考
iOS 關(guān)于MVC和MVVM設(shè)計(jì)模式的那些事
雜談: MVC/MVP/MVVM
本文是我對MVC的一些理解及認(rèn)知许布,如果有問題歡迎提問,如有錯(cuò)誤绎晃,歡迎指正蜜唾,水平有限,犯錯(cuò)難免箕昭,不喜勿噴灵妨。本文為原著,如果轉(zhuǎn)載請注明出處落竹,謝謝泌霍。