iOS - 开发1年后对MVC新的理解
一·Controller层
先上代码
- @interface Controller()
- @property(nonatomic, strong) UITableView *tableView;
- @end
-
- @implementation Controller
-
- - (void)viewDidload {
- [super viewDidload];
- 初始化UI
- 懒加载
- [self.view addSubView:xxxx];
- }
-
- - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
- _cell = [tableView dequeueReusableCellWithIdentifier:addCellId forIndexPath:indexPath];
-
- _cell.model = [APIManager manager].backPackModel[indexPath.row];
-
- return _cell;
- }
-
- - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
- [tableView deselectRowAtIndexPath:indexPath animated:YES];
- }
-
- - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
- return self.dataArray.count;
- }
-
-
- - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
- MVCTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuserId forIndexPath:indexPath];
- // 不合理
- cell.model = self.dataArray[indexPath.row];
- return cell;
- }
-
- 相信很多人都有写过这段代码 delegate & dataSource 代理方法 导致了VC的沉重
- @end
-
问题来了回到工程项目需求,每个cell都有增加减少按钮 而且需要有选中效果在View层.意味着MVC架构模式中 我在View层对Model进行了修改.
再者Action操作视图逻辑代码是放在C层或V层 最终导致C层随着需求会慢慢变大变臃肿
再回过头来看项目工程目录导致VC过重的原因因素
·繁重的UI 例如tableView CollectionView
·业务逻辑 如下面的全选效果
·网络请求
·代理方法
优化代码
- 封装一个继承自NSObject的类,遵循DataSource代理
- @interface Controller()
- @property(nonatomic, strong) UITableView *tableView;
- @property(nonatomic, strong) YourDataSource *dataSource;
- @end
-
- @implementation Controller
-
- - (void)viewDidload {
- self.dataSource = [YourDataSource alloc]initWithIdentifier:reuserId
- configBlock:^(yourCellClass *cell, Model *model, NSIndexPath *indexPath) {
- cell.model = model; //这一句代码代表了MVC模式中的 Model->View 这一单层通道
- }
- [self.view addSubview:self.tableView];
- self.tableView.dataSource = self.dataSource;
- [self.dataSource addDataArray:[SingleManager manage].userListArr];
- }
- @end
-
- 到这减少了 DataSource 所需要实现的代码
-
- 这里不给出DataSource的封装代码 需要源码的可以加我 936101005 点个赞 ~ 拒绝伸手
二·Model层
Model层玩法就很多了,这里我通常使用单例保存在内存中看 -> 《iOS-使用GCD单例创建管理对象》
第二种玩法是通过Swift混编,我们来对比一下Model层的不同
定义
- OC层
-
- @interface Model : NSObject
- @property (nonatomic, copy) NSString *name;
- @property (nonatomic, copy) NSString *imageUrl;
- @property (nonatomic, copy) NSString *num;
- @property (nonatomic, strong) NSArray <UserModel *> *userList;
- @end
-
- @interface UserModel : NSObject
- @property (nonatomic, copy) NSString *userId;
- @property (nonatomic, copy) NSString *group;
- @end
-
- Swift层
-
- 先定义结构体 model结构
- struct ModelBean: Coable {
- let name: String
- let imageUrl: String?
- let num: Int
- let userList : [UserModelBean]
- }
-
- struct UserModelBean: Coable {
- let userId: String
- let group: String
- }
解析
我的上一篇文章写了关于数据与模型绑定的文章《NSArray与Model模型》,不熟悉的可以回过头看一看。
- OC层
-
- for (int i = 0; i<temArray.count; i++) {
- Model *m = [Model modelWithDictionary:temArray[i]];
- [self.dataArray addObject:m];
- }
-
- 但通常的,我会使用MJExtention封装好的三方框架来解析数据
- mj_objectArrayWithKeyValuesArray
- Swift层
-
- 通常解析数据会根据后台的数据结构返回来的JSON进行匹配
- swift会有点麻烦 我们拿一个 数据里嵌套数组来作为例子
-
- @objc public class Model: NSObject {
- init(bean: ModelBean) {
- name = bean.name
- imageUrl = bean.imageUrl
- num = bean.num
- userList = bean.userList?.map{ .init(bean: $0)} ?? []
- }
- @objc public let name: String
- @objc public let imageUrl: String?
- @objc public let num: Int
- @objc public let userList : [UserModelBean]
- }
-
- @objc public class UserModelBean: NSObject {
- init(bean: UserModelBean){
- userId = bean.userId ?? ""
- gourp = bean.gourp ?? ""
- }
- @objc public let userId: String?
- @objc public let gourp: String?
- }
-
-
三·View层
MVC架构中model层数据传给cell通过setter与数据进行通信
- @interface Cell : UITableViewCell
- @property (nonatomic, strong) Model *model;
- @end
-
- @implementation Cell
-
- 但是外界数据模型没有一起变化, 暴露一个接口给外界刷新UI 导致了高偶合问题
- - (void)setNum:(int)num {
- _num = num;
- self.model.num = self.numLabel.text;
- }
-
- View 和 Model 进行了绑定 //到此双向绑定完成
- - (void)setModel:(Model *)model { //setter Model 代表了MVC架构中的View -> Model view展示model数据
- _model = model;
- self.num = model.num;
- self.Name = model.name;
- self.xxx = model.xxx;
- }
- @end
四·MV? 架构 (Model和UI之间的消息通讯方式)
在上面我们把DataSource麻烦的代码交给了封装的DataSource类中,但是还剩下一个Delegate代理没解决
MVC (最快上手架构)
MV-Protocal (适用于复杂的多层回调)
MV-Block (适用于简单的单层回调)
创建一个数据提供层Present 也可以成为称为代理,来减少Controller的负担
- @protocal PresentDelegate <NSObject>
-
- - (void)addBtnWithNum:(int)num indexPath:(NSIndexPath *)indexPath;
-
- - (void)reloadUi;
-
- @end
-
- @interface Present: NSObject <PresentDelegate>
- @property (nonatomic, weak) id<PresentDelegate> delegate;
- @end
-
- @implementation Present
-
- - (void)addBtnWithNum:(int)num indexPath:(NSInexPath *)indexPath {
- @synchronized (self) {
- if (indexPath.row < [SingleManage manage].userList.count) {
- Model *model = [SingleManager manage].userListArr[indexPath.row];
- model.num = num;
- }
- }
-
- if (self.delegate && [self.delegate respondsToSelector:@selecotr(reloadUI)]) {
- [self.delegate reloadUI];
- }
- }
- @end
View层的setter方法就可以优化成
- @interface Cell: UITableViewCell
-
- @property (nonatomic, weak) id<PresentDelegate> delegate;
- @property (nonatomic, strong) NSIndexPath* indexPath;
- @end
-
-
- @implementation
- - (void)setNum:(int)num {
- _num = num;
- if (self.delegate && [self.delegate respondsToSelector:@selector(didClickNum:indexPath:)]) {
- [self.delegate didClickNum:self.numLabel indexPath:self.indexPath];
- }
- }
-
- @end
那么最终Controller层可以写成这样
- __weak typeof(self) weakSelf = self;
- self.dataSource = [[DataSource alloc] initWithIdentifier:reuserId configureBlock:^(MVPTableViewCell *cell, Model *model, NSIndexPath *indexPath) {
- // 代理 model --> UI
- cell.numLabel.text = model.num;
- cell.nameLabel.text = model.name;
- cell.indexPath = indexPath;
- cell.delegate = weakSelf.pt;
- }];
- [self.dataSource addDataArray:[SingleManager manager].userList];
-
- // UI
- [self.view addSubview:self.tableView];
- self.tableView.dataSource = self.dataSource;
- self.pt.delegate = self;