请写一篇关于【iOS应用架构设计:MVC、MVVM、VIPER对比】的技术文章

郑刚
2025-12-19 11:54
阅读 390

作者:一个刚从外包跳进甲方、在杭州买了小房子还着房贷的Java开发
字数:3926字
关键词:代码人生、运营


去年十月,我还在滨江那间不到30平米的出租屋里加班到凌晨两点。

窗外是钱江新城的霓虹灯,屋里是我泡了第三遍的枸杞茶。手机屏幕亮起,老婆发来消息:“今天又没回家吃饭?”
我回了个“嗯”,手指悬在键盘上,却不知道怎么加一句“对不起”。
那天晚上,我在改一个上线三天就崩了五次的订单模块——不是逻辑错,是整个架构像一锅乱炖的方便面,View Controller里塞满了网络请求、数据解析、埋点上报,甚至还有几行测试用的print("debug here")

当时真的很焦虑。

三年外包生涯,我写的Java后端接口被产品经理夸过“稳定”,但每次看到前端同事(尤其是iOS组)为了赶需求把ViewController写成“上帝类”,我就替他们捏把汗。可我又能说什么?我们只是乙方,甲方爸爸说“下周三必须上线”,我们就得把屎山代码硬塞进去跑起来。

直到今年三月,我终于跳槽进了这家做本地生活服务的甲方公司。月薪从15k涨到22k,最重要的是——我终于能参与产品架构决策了。更巧的是,公司正要重构核心App,iOS端技术债压得团队喘不过气,CTO拍板:“这次必须选个靠谱的架构,不能再糊弄了。”

于是,我和iOS组的老张(比我大五岁,已婚有娃,每天准点六点走,堪称卷王克星)一起,花了两周时间,把MVC、MVVM、VIPER三种主流架构在真实业务场景里跑了一遍。

这篇文章,就是那次“架构实战”的复盘。不讲理论套话,只聊代码人生里的真实选择,以及一个刚上岸的程序员对“运营”二字的新理解。


起点:那个让人又爱又恨的MVC

说实话,刚接触iOS时我也觉得MVC挺香。

Apple官方文档推它,Xcode模板默认就是它,ViewController里放点逻辑似乎也“没那么糟”。我们外包时期80%的项目都用MVC——毕竟甲方要快,谁有空搞分层?

但问题很快暴露。比如我们做过一个外卖商家后台App,首页要展示订单列表、营业状态、实时营收、客服入口。老版本用MVC,ViewController代码飙到1200行:

class HomeViewController: UIViewController {
    // MARK: - Outlets
    @IBOutlet weak var orderTableView: UITableView!
    @IBOutlet weak var revenueLabel: UILabel!
    
    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        fetchOrders()
        fetchRevenue()
        setupTableView()
        checkBusinessStatus()
        registerForPushNotifications()
        logPageView() // 埋点
    }
    
    // 网络请求、数据处理、UI更新全混在一起...
}

痛点在哪?

  • 测试难:想单独测“营收计算逻辑”?得启动整个ViewController。
  • 复用难:另一个页面也要营收数据?复制粘贴?还是抽个Util?没人敢动。
  • 协作卡:UI改个颜色,可能误删一行网络回调,直接线上Crash。

老张吐槽:“这哪是Model-View-Controller,这是Massive-View-Controller!”

但我们不能全盘否定MVC。它在简单页面、快速原型场景下依然高效。比如一个纯展示的活动页,数据静态、交互少,硬套VIPER反而过度设计。

结论:MVC适合“一次性”或“低复杂度”功能,但一旦涉及多数据源、复杂状态、频繁迭代,它就会变成技术债加速器。


进化:MVVM让逻辑和界面解耦

既然MVC扛不住,我们试了MVVM。

核心思想很简单:ViewModel负责业务逻辑和数据绑定,View只管渲染。配合RxSwift或Combine,数据流清晰多了。

我们拿“订单详情页”做试点。旧版MVC里,这个页面有状态机(待接单/配送中/已完成)、地图定位、骑手信息轮询、用户评价弹窗……乱成一锅粥。

用MVVM重构后:

class OrderDetailViewModel {
    let order = BehaviorRelay<Order?>(value: nil)
    let riderLocation = PublishSubject<CLLocation>()
    let pageActions = PublishSubject<OrderAction>()
    
    func loadOrder(id: String) {
        api.getOrder(id).subscribe(onNext: { order in
            self.order.accept(order)
            if order.status == .delivering {
                self.startRiderTracking()
            }
        }).disposed(by: disposeBag)
    }
}

class OrderDetailViewController: UIViewController {
    @IBOutlet weak var statusLabel: UILabel!
    
    override func bindViewModel() {
        viewModel.order.map { $0?.statusText }.bind(to: statusLabel.rx.text).disposed(by: disposeBag)
    }
}

好处立竿见影

  1. 测试友好:ViewModel可以脱离UI单元测试,模拟各种订单状态。
  2. 逻辑集中:所有状态转换、副作用都在ViewModel里,不再散落在ViewController各处。
  3. 响应式体验:数据变化自动驱动UI,减少手动reloadData()的遗漏。

但MVVM也有坑。

首先是学习成本。团队里两个新人之前只写过MVC,看到ObservableDisposeBag就懵了。老张花了一周带他们,期间还因为忘记disposed(by:)导致内存泄漏。

其次是过度响应式。有次为了一个简单的按钮点击,硬套PublishSubject,结果代码比MVC还啰嗦。后来我们定了条规矩:简单交互用闭包,复杂状态流才用响应式

最关键的是——MVVM没解决导航问题。页面跳转逻辑还是写在ViewController里,大型App里依然会形成“跳转蜘蛛网”。

不过总体而言,MVVM是我们目前最推荐的平衡方案:比MVC清晰,比VIPER轻量,适合大多数中等复杂度业务。


终极武器?VIPER的仪式感与代价

当CTO听说我们在试MVVM,他幽幽地说了一句:“要不要试试VIPER?隔壁金融组用它做了交易系统,零线上事故。”

于是我们咬牙上了VIPER。

VIPER把组件拆得极其彻底:View、Presenter、Interactor、Router、Entity,每个角色职责单一,连数据传递都要通过协议(Protocol)。

我们拿“支付流程”做实验——涉及金额校验、风控检测、支付渠道选择、结果回调,安全性和可维护性要求极高。

结构长这样:

PayView → PayPresenter → PayInteractor → (调用API) → PayRouter

优点确实硬核

  • 极致解耦:View完全不知道业务逻辑,Interactor专注数据处理。
  • 可测试性拉满:每个模块都能Mock,连Router跳转都能验证。
  • 团队协作清晰:UI同学只改View,后端对接只碰Interactor。

但代价也肉眼可见:

  • 代码量爆炸:一个简单页面要建5个文件,写8个协议。
  • 调试路径变长:点个按钮,要查View→Presenter→Interactor→API,日志都得串起来看。
  • 新人上手慢:实习生第一天看VIPER代码,直接问:“这项目是不是用了什么自动生成工具?”

老张最后摇头:“VIPER适合银行、医疗这类高可靠性、低迭代频率的场景。咱们做本地生活,每周都要改UI、加活动,搞VIPER等于给自己戴镣铐跳舞。”

我们最终只在支付、登录等核心模块用了VIPER,其他地方回归MVVM。


架构之外:代码人生与运营思维

折腾完这三套架构,我最大的感悟不是技术选型,而是**“架构服务于业务”**。

以前在外包,我们只关心“功能能不能跑通”;现在在甲方,我开始思考:

  • 这个功能未来三个月会怎么变?
  • 运营同学下周会不会要加个弹窗?
  • 客服反馈的某个问题,是不是架构缺陷导致的?

比如上周五晚上,运营负责人冲进技术部:“双十一大促要加个红包雨!周一给设计稿,周三必须上线!”
如果还是MVC架构,我肯定连夜改代码;但现在用MVVM,我们直接在ViewModel里加个redPacketStream,View层复用现有Banner组件——两天搞定,零风险

那一刻我才懂,所谓“运营”,不只是发推送、搞活动,更是对系统弹性和扩展性的持续投资。而好的架构,就是给运营留出“快速试错”的空间。


写在最后:一个普通程序员的选择

回望这三年,从外包到甲方,从写接口到参与架构,我的代码人生正在从“实现需求”转向“设计系统”。

房贷每月6800,不敢轻易裸辞;老婆说想换学区房,我只能苦笑。但至少,我现在写的每一行代码,都不再是“用完即弃”的消耗品。

MVC、MVVM、VIPER没有绝对好坏,只有是否匹配你的业务阶段、团队能力和产品节奏

如果你在创业公司求快,MVC+严格Code Review也能活; 如果你在做内容型App,MVVM大概率是甜点; 如果你在搞金融或IoT,VIPER的严谨值得投入。

重要的是——别让架构成为枷锁,而要让它成为杠杆

上周搬家,我把外包时期的工牌扔了。新工牌上印着“高级开发工程师”,其实心里清楚:我只是个更谨慎的码农罢了。

但至少,现在的我,能对着产品经理说一句:“这个需求,我们可以用更优雅的方式实现。”

这就够了。


共勉

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝