iOS MVP模式下通过Segue在多ViewController间传值的问题咨询
问题解答
相关代码参考
class HomeVC: UIViewController { var homeData: [WorkoutModelInfo]= [] override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "WorkoutDetailsViewController" { if let homeDetailVC = segue.destination as? HomeDetailsVC { guard let selectedIndex = sender as? Int else { return } let titleInSender = homeData[selectedIndex].workoutName homeDetailVC.presenter.convertData(homeData: home[selectedIndex]) } } } } class HomeDetailsPresenter: class { var titleValue: String = "" var workoutDetailsData = [HomeDetailsData]() var dateOfWorkout: String = "" func convertData(workoutModel: WorkoutModelInfo) { var exercises = [ExerciseCellInNewWorkout]() for row in 0..<workoutModel.exercises.count { exercises.append(ExerciseCellInNewWorkout(opened: false, workoutModel: workoutModel.exercises[row])) } workoutDetailsData = exercises titleValue = workoutModel.workoutName dateOfWorkout = workoutModel.workoutDate } } class HomeDetailsVC: UIViewController { lazy var presenter = HomeDetailsPresenter(homeDetailsPresenterDelegate: self) // 其他代码省略 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let destinationVC = segue.destination as? ThirdVC { destinationVC.workoutNameText = presenter.titleValue destinationVC.workoutDateText = presenter.dateOfWorkout destinationVC.data = presenter.workoutDetailsData } } }
问题1:Segue的prepare方法中直接调用目标VC的presenter方法是否符合规范?
- 结论:不符合规范,不推荐这么写
这种写法直接破坏了视图控制器的封装性,HomeVC强依赖了HomeDetailsVC的内部实现细节(内部持有presenter、presenter存在convertData方法),会导致两个模块耦合度过高:
- 后续如果HomeDetailsVC做架构重构,比如替换为MVVM架构移除presenter,或者修改convertData的方法名、参数,所有调用它的上游页面都要同步修改,维护成本极高
- 不符合「模块间仅通过公开接口交互」的设计原则,HomeDetailsVC的内部组件不应该对外暴露
- 推荐优化方案:给HomeDetailsVC增加公开的接收参数属性,内部自行处理和presenter的交互:
// HomeDetailsVC 新增公开属性,隐藏内部实现 class HomeDetailsVC: UIViewController { // 对外仅暴露该接收参数,外部无需关注内部架构 var workoutModel: WorkoutModelInfo? { didSet { guard let model = workoutModel else { return } presenter.convertData(workoutModel: model) } } lazy var presenter = HomeDetailsPresenter(homeDetailsPresenterDelegate: self) // 其他代码省略 } // HomeVC的prepare方法简化为仅传公开参数 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "WorkoutDetailsViewController" { if let homeDetailVC = segue.destination as? HomeDetailsVC { guard let selectedIndex = sender as? Int else { return } homeDetailVC.workoutModel = homeData[selectedIndex] } } }
问题2:MVP模式下直接从presenter读属性赋值给目标VC是否正确?
- 结论:常规场景下是符合规范的,不需要额外加中间变量
MVP架构中,ViewController本身的核心职责就是和持有它的Presenter交互、获取展示数据、处理用户交互,所以VC直接读取Presenter的可公开访问属性完全符合架构设计要求,不需要多此一举在VC层存储冗余的中间属性。
只有满足以下场景时,才推荐在VC层声明变量、通过代理更新后再传递:
- 要传递的数据会在页面生命周期内动态变化,比如Presenter会异步请求更新标题、数据列表,需要确保跳转时拿到的是最新值,此时通过Presenter代理回调VC存储最新值更稳妥
- 数据需要在当前页面做二次处理/缓存,多次跳转都要用到同一个值,存在VC层可以避免重复从Presenter读取/处理
- 注意点:你当前的写法没有把HomeDetailsVC的Presenter直接传递给ThirdVC,只是把数值赋值给ThirdVC的公开属性,不存在跨页面依赖Presenter的问题,完全符合分层原则。
内容的提问来源于stack exchange,提问作者TeddyKen
相关产品推荐
相关产品推荐

