让iOS App跑得更快这件事我折腾了两年

开发者晨报
2026-08-24 23:38
阅读 756

在成都做推荐算法这两年,我其实挺少碰iOS原生开发的。但架不住组里iOS同学离职后,老板拍着我肩膀说“你以前不是写过Swift吗”。于是我就这么半推半就地接过了App性能优化这摊子事。真扎进去之后发现,iOS性能优化跟推荐系统里做延迟优化是一个道理:你得先搞清楚时间都花哪儿了,再决定动哪里。

从一次线上事故说起

上个月App更新到3.8版本后,用户反馈首页Feed流卡得跟PPT似的。测试同学用iPhone 11复现了一下午,定位到列表页滚动时频繁触发离屏渲染。那天晚上我对着Instruments的Core Animation模板盯到凌晨一点,发现Cell里的圆角头像用cornerRadiusmasksToBounds实现,复用的时候还不停重新设置阴影。两个操作叠加,GPU离屏渲染开销直接拉满。

修起来不难:头像改成预渲染的圆角图片缓存,阴影用shadowPath固定,别让系统每次都去算。改完再跑,掉帧从平均18fps回到接近58fps。就这十几行代码的事,折磨了用户两个礼拜。

启动优化不能只盯着main函数

我们App冷启动在低端机型上要2.8秒。用App Launch模板跑了几个场景,发现大头不在main之后,而在动态库加载和+load方法上。接了一堆第三方SDK,有些SDK的+load里做了大量不该做的事,比如有个统计SDK在+load里初始化了数据库连接,同步执行直接拖了400多毫秒。

我的做法是把非关键初始化全部挪到首屏渲染完成后异步执行。在application(_:didFinishLaunchingWithOptions:)里只做最核心的几件事,剩下的丢给DispatchQueue.main.async,或者用Task延迟到viewDidAppear之后再跑。动态库能合并的合并,能静态链接的就静态链接。这一轮下来,冷启动降到了1.9秒左右。

内存和能耗是隐形的坑

大家讨论性能优化时眼睛都盯着帧率和启动时间,内存和能耗反而容易被忽略。我们App有个拍照识别功能,用户拍完照片先在本机做压缩和预处理,再上传到AWS S3,后端用CV模型推理后返回结果。但本机预处理那步每次拍照都重新创建一个CIContext——这玩意儿创建成本很高,不释放的话内存会持续涨。连续拍十几张之后,内存占用能飙到800MB以上,iPhone 13都能被杀后台。

后来把CIContext改成单例复用,并用autoreleasepool把大块图像数据处理包起来,内存占用稳定在300MB以内。端上的每一步操作都得抠,用户不会想到瓶颈其实在自己手机上。

SwiftUI的坑比想象中多

新页面用SwiftUI,老页面是UIKit。混合开发的好处是开发快,坏处是性能问题出在哪一层很难定位。我踩过一个坑:一个SwiftUI页面里有个@State变量每秒钟更新一次(显示倒计时),结果整个页面的body都在跟着刷新,包括一个很重的图片网格。后来把倒计时拆到独立子视图里,用EquatableView包一下,再配合.task修饰符控制更新范围,CPU占用从45%降到6%左右。

SwiftUI是声明式的,系统帮你做了很多优化,但一旦状态管理没做好,重建范围失控,性能反而更差。别迷信“新技术一定快”,该用Instruments还是得用。

上架前别忘了这些事

我们之前有个版本因为启动时间过长被拒了,审核团队原话是“App took too long to launch on iPad”。后来发现是iPad上加载了一个不必要的全屏引导页,还同步请求了网络数据。去掉之后就好了。

提交审核前至少用低端真机跑一遍完整启动流程,别只在模拟器上自嗨。模拟器性能跟真机差距很大,尤其是GPU相关操作。另外,Xcode Organizer里有个“Performance”面板,能看到线上用户的启动时间和内存数据。我们靠这个数据发现线上还有一批iPhone 8用户启动时间依然在2.5秒以上,后来又针对老机型做了一轮降级处理。

写在最后

这两个月从被赶鸭子上架到能独立完成一轮性能优化,最大的感受是:iOS性能优化没有银弹,工具链是现成的,关键是你愿不愿意花时间去看数据、做实验。跟我做推荐算法AB测试的思路完全一致——先测量,再优化,最后验证。别凭感觉改代码,不然你都不知道自己到底优化了什么。

如果你也在做iOS性能优化,别急着改代码,先把Instruments打开,看看时间到底花在哪儿了。这跟后端排查延迟问题一样,先看火焰图,再动手。共勉。

评论 0

最热最新
暂无评论
开发者晨报Lv.1
0
影响力
0
文章
0
粉丝