在iOS View中执行页面跳转是否违反MVC架构?两种实现方式探讨
方式二是否合理?答案是:不合理,你的判断完全正确,它确实违反了MVC架构的核心设计原则,还会带来不少潜在问题
咱们先从MVC的职责划分说起:
- View层(这里的CollectionViewCell、嵌套的CollectionView都属于View)的核心职责是展示UI、响应用户交互并传递事件,它不应该直接参与业务逻辑处理(比如页面跳转),更不应该直接依赖或操作Controller层。
- Controller层的职责才是接收View传递的事件、处理业务逻辑、管理页面跳转。
方式二的具体问题:
- 耦合度极高:你的Cell代码里硬编码了
CWFSpecialSaleViewController,相当于把Cell和这个特定的Controller绑定死了。以后如果这个Cell要用到其他页面,你必须修改Cell内部的代码,完全破坏了View的复用性。 - 稳定性差:
getCurrentViewController通过遍历视图层级找Controller的方式非常脆弱——如果后续页面的视图结构调整(比如Cell的父容器View变了),这个方法可能找不到正确的Controller,甚至返回nil,导致跳转失败或者崩溃。 - 维护成本高:正常情况下,页面跳转的逻辑应该集中在Controller里,现在你把这部分逻辑放到了Cell中,以后排查跳转相关的问题时,很容易忽略Cell里的代码,增加调试难度。
对比方式一的优势:
方式一的代理模式完全符合MVC的设计:
- 职责清晰:Cell只负责“告诉外部用户点击了某个商品”,至于点击后要做什么(跳转详情页、弹窗提示等),完全由代理(也就是Controller)来决定。
- 低耦合:Cell不需要知道代理具体是哪个Controller,只要对方遵守代理协议就行。后续换个Controller使用这个Cell,只要实现对应的代理方法即可,不需要修改Cell的代码。
- 易维护:所有业务逻辑都集中在Controller层,出问题时定位更直接。
如果你觉得代理写起来有点繁琐,还可以用Block回调替代(本质和代理一致,都是事件传递):
在Cell里定义一个Block属性:
@property (nonatomic, copy) void(^goodsItemClickedBlock)(NSString *goodsID);
然后在didSelectItemAtIndexPath里触发Block:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { NSString *goodsID = self.model.goods[indexPath.row]; if (self.goodsItemClickedBlock) { self.goodsItemClickedBlock(goodsID); } }
最后在Controller配置Cell时设置Block:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { LimitTimeSaleCell *cell = [tableView dequeueReusableCellWithIdentifier:@"LimitTimeSaleCell" forIndexPath:indexPath]; cell.model = self.dataArray[indexPath.row]; __weak typeof(self) weakSelf = self; cell.goodsItemClickedBlock = ^(NSString *goodsID) { [weakSelf gotoGoodsDetailViewControllerWithGoodsID:goodsID]; }; return cell; }
这种方式比代理更简洁,同时也保持了MVC的分层原则。
总结一下:方式二的实现方式是不可取的,建议坚持用代理或Block这类事件传递方式,保持View和Controller的职责分离,这样你的代码会更健壮、更易维护。
内容的提问来源于stack exchange,提问作者鏃犲涔嬫槦杈�,ios;objective-c;model-view-controller;uicollectionview"
相关产品推荐
相关产品推荐

