返回文章列表

iOS 性能优化 系列 04|图片加载与缓存,为什么总是性能问题高发区

图片问题真正麻烦的不是“要不要缓存”,而是下载、解码、缩放、内存占用和显示时机全都缠在一起

图片几乎是 iOS 项目里最容易反复出性能问题的地方。

你会在各种场景里遇到它:

  • 首页卡顿
  • 列表滚动掉帧
  • 内存飙升
  • 首屏加载慢
  • 滚动时图片一闪一闪
  • 同样一页内容,为什么图片一多就明显更慢

很多人一开始会把图片问题简化成一句话:

那就加缓存。

缓存当然重要,但如果只把图片优化理解成“有没有缓存”,通常离真实问题还差很远。
因为图片链路真正复杂的地方在于,它把很多成本叠在了一起:

  • 下载
  • 解码
  • 缩放
  • 渲染
  • 内存持有
  • 生命周期管理

所以图片问题几乎永远不是单点问题,而是一个系统问题。

一、为什么图片比普通数据更容易把问题放大

因为图片天生具备几个让性能变脆弱的特点:

  • 资源体积通常更大
  • 展示前往往还要解码
  • 不同尺寸设备和场景会触发缩放
  • 列表中会高频出现
  • 很容易被缓存并长期占内存

也就是说,图片不是“多一个字段”,而是一类对 CPU、内存、带宽、渲染都同时施加压力的资源。

这就是为什么图片一旦处理不好,经常不会只表现成一个问题,而是多个问题一起出现:

  • 滚动卡
  • 内存高
  • 首屏慢

二、下载不是唯一成本,很多团队真正低估的是解码时机

很多人一提图片加载,注意力都放在下载上:

  • 图片大不大
  • 网络慢不慢
  • 缓存命中没有

但真实项目里,下载往往不是唯一成本,甚至经常不是滚动卡顿的核心成本。
真正容易在关键时刻拖慢体验的,常常是:

  • 图片解码
  • 显示前尺寸处理
  • 滚动时主线程上发生的图像准备工作

也就是说,一张图片“下载完了”不等于“已经适合显示”。
如果你把很多图像处理工作压到显示瞬间,页面就很容易在最敏感的时候掉帧。

三、缓存也不是越多越好,缓存策略本身可能制造新问题

缓存是必要的,但缓存从来不是无成本的。

如果缓存策略不清楚,常见后果包括:

  • 明明命中缓存了,内存却越来越高
  • 磁盘缓存太激进,清理策略不合理
  • 同一张图以多个尺寸反复缓存
  • 列表快速切换时缓存命中看似不错,但页面仍然卡

这说明缓存真正要解决的不只是“少下一次”,还包括:

  • 缓存什么尺寸
  • 保留多久
  • 什么时候该淘汰
  • 哪些资源适合内存缓存,哪些只适合磁盘缓存

所以图片缓存从来不是一句“加缓存”就能讲清楚的,它本质上是一套资源生命周期策略。

四、列表里的图片为什么特别容易成为卡顿源头

因为列表场景天然满足几个高风险条件:

  • 同屏图片数量多
  • 滚动频率高
  • 复用和回收频繁发生
  • 用户对掉帧非常敏感

如果在这个场景里你还同时做这些事:

  • 图片解码
  • 尺寸计算
  • 圆角裁剪
  • 富文本叠层

那主线程就很容易在滚动中被压垮。

所以列表图片优化最关键的一条,不是“有没有缓存”,而是:

这些工作是不是发生在滚动关键路径上。

如果发生在关键路径上,再小的额外成本,放到高频滚动里都会被放大。

五、一个很常见的误区:把所有图片问题都归结成网络问题

真实项目里,经常会有人说:

  • 图片多,所以慢
  • 图片慢,所以是网络问题

这条推理经常不完整。

因为很多图片问题即使在强网、缓存命中情况下仍然存在,原因就在于:

  • 图片已经拿到了,但没被提前解码
  • 图片尺寸不合适,显示前又做了额外处理
  • 列表滚动时状态变化过多,引发大量重绘

所以图片问题里,“资源获取”只是链路的一部分。
真正的难点在于:这条链路的每一步是否都发生在合理时机。

六、优化图片链路时,我最常问的几个问题

如果今天我要排一个图片相关性能问题,我通常会先问:

  1. 问题是首屏慢、列表卡,还是内存高。
  2. 图片是不是在显示瞬间才开始承担高成本处理。
  3. 当前加载的是不是合适尺寸,而不是原图硬缩。
  4. 缓存命中了什么,命中的到底是原图、处理后图,还是根本没命中关键尺寸。
  5. 图片链路是不是把太多工作压在了主线程。

这几个问题比“换个库试试”更有价值,因为它们直接逼近了真实成本。

七、结论:图片问题高发,不是因为图片特殊,而是因为它天然跨越了多种资源维度

如果只用一句话总结,我会说:

图片加载与缓存之所以总是性能问题高发区,不是因为“图片很难”,而是因为它同时牵涉带宽、CPU、内存、渲染时机和生命周期管理,一旦任意一环设计不合理,问题就会被迅速放大。

所以图片优化真正要做的,不只是缓存,而是把:

  • 下载
  • 解码
  • 缩放
  • 缓存
  • 显示时机

这整条链路放在一起看。