一、前言:90% iOS 项目的缓存乱象,全是认知错误
做 iOS 开发几乎没人能绕开网络缓存,但日常项目中,绝大多数开发者对缓存的认知只停留在「存个沙盒、下次读取」,导致线上层出不穷的缓存疑难问题:
接口数据更新了,App 一直展示旧缓存,用户刷不出新内容
断网白屏、弱网加载缓慢,体验极差,竞品却能秒开页面
部分接口走缓存、部分接口不生效,缓存规则混乱无法统一管控
缓存无限堆积,导致 App 体积越来越大、缓存垃圾无法清理
手动写缓存逻辑冗余重复,每个业务都要单独判空、存数据、读数据
想做「先展示缓存、再刷新最新数据」的无感刷新,始终实现不完美
很多人分不清:HTTP 协议缓存、NSURLSession 系统缓存、业务层本地缓存三者的区别,盲目混用导致缓存失效、脏数据、更新延迟。
本文结合多年一线项目优化经验,从零拆解 iOS 整套缓存体系:底层原理、系统缓存机制、协议缓存规则、自定义缓存封装、无感刷新架构、高频踩坑复盘,搭配全套可复用代码、业务场景案例、面试核心考点,彻底打通 iOS 网络缓存底层与工程落地。
二、核心认知:iOS 三层缓存架构(必懂分层逻辑)
iOS 网络缓存不是单一逻辑,是三层自上而下的完整体系,每层职责、优先级、适用场景完全不同,这是解决所有缓存问题的核心前提:
1. 三层缓存分层定义
第一层:HTTP 协议缓存(服务端控制):遵循 RFC 标准,通过 Response 响应头(Cache-Control、ETag、Last-Modified)控制缓存有效期、校验规则,是跨端统一的缓存标准
第二层:NSURLSession 系统缓存(系统层):基于
NSURLCache实现,自动接管 HTTP 协议缓存,内存+磁盘双缓存,无需手动读写,NSURLSession/AFN 底层默认生效第三层:业务本地缓存(客户端自定义):基于沙盒/YYCache/数据库实现的自定义缓存,完全脱离系统限制,可自由控制缓存粒度、过期时间、清理规则,适配复杂业务场景
2. 分层核心区别(实战关键)
HTTP缓存、系统缓存适合静态数据、稳定接口;自定义本地缓存适合动态业务数据、个性化页面、需要精准控权的场景,三者互补才能实现最优体验。
三、第一层:HTTP 协议缓存底层原理(服务端+客户端联动)
HTTP 缓存是所有网络缓存的基石,分为强缓存和协商缓存两种机制,优先级:强缓存 > 协商缓存。无需客户端写代码,只要响应头配置正确,NSURLSession 会自动识别并执行。
1. 强缓存(无需请求服务器,直接读本地缓存)
强缓存生效时,客户端不会发起任何网络请求,直接读取本地缓存数据,状态码 200(from cache),速度极致最快。
依靠两个响应头控制:
Cache-Control(现代主流,优先级最高):max-age=3600 代表缓存有效期1小时
Expires(老旧兼容):绝对过期时间,受客户端时间篡改影响,现已逐步淘汰
实战案例1:首页静态文案、Banner图缓存
服务端配置响应头:Cache-Control: max-age=86400,24小时内重复请求直接读缓存,无需网络请求,大幅减少接口压力、提升页面打开速度。
强缓存致命缺点:有效期内,无论服务端数据如何更新,客户端永远读取旧数据,无法实时更新。
2. 协商缓存(询问服务器,按需更新)
强缓存过期后,不会直接请求新数据,而是发起轻量校验请求,询问服务器数据是否更新:
数据未更新:返回 304 Not Modified,客户端复用本地缓存
数据已更新:返回 200 + 最新数据,客户端更新本地缓存
协商缓存两组配对规则(自动触发):
ETag / If-None-Match(优先级高、精准校验):服务端返回文件唯一指纹,客户端下次请求携带指纹,服务端比对指纹是否一致
Last-Modified / If-Modified-Since(兼容方案):基于文件最后修改时间校验,精度秒级,无法识别秒内多次修改
3. 协议缓存完整执行链路(项目真实流程)
首次请求:客户端请求数据 → 服务端返回数据+缓存头 → 系统自动存入缓存
二次请求:优先判断强缓存是否有效 → 有效直接读取 → 失效走协商缓存 → 304复用缓存/200更新缓存
四、第二层:NSURLSession 系统缓存 NSURLCache 底层实战
iOS 中所有基于 NSURLSession 的网络请求(AFN、原生请求),默认由NSURLCache接管 HTTP 协议缓存,实现内存缓存+磁盘缓存双缓存机制,开发者无需手动存储。
1. NSURLCache 双缓存机制
内存缓存:读写速度极快,App 运行期间常驻,重启 App 丢失
磁盘缓存:持久化沙盒存储,重启 App、重启手机依然保留,过期自动失效
2. 系统默认缓存策略(NSURLRequestCachePolicy)
iOS 提供7种缓存策略,4种常用核心策略,适配绝大多数业务场景:
缓存策略 | 核心逻辑 | 适用场景 |
|---|---|---|
UseProtocolCachePolicy(默认) | 完全遵循HTTP协议,强缓存优先,过期走协商缓存 | 常规接口、静态资源 |
ReloadIgnoringLocalCacheData | 忽略所有缓存,强制实时请求网络 | 实时数据、支付、个人中心、敏感数据 |
ReturnCacheDataElseLoad | 优先读缓存,无缓存再请求网络 | 离线可用页面、内容稳定性页面 |
ReturnCacheDataDontLoad | 只读缓存,绝不请求网络 | 离线专属页面、历史记录 |
3. 全局缓存配置(项目初始化必备)
系统默认缓存空间极小,极易导致缓存失效、被自动清理,项目必须手动配置缓存容量:
// AppDelegate 初始化配置全局缓存 - (void)configNetworkCache { // 内存缓存4M,磁盘缓存20M,可根据项目调整 NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:4*1024*1024 diskCapacity:20*1024*1024 diskPath:nil]; [NSURLCache setSharedURLCache:cache]; }func configNetworkCache() { let cache = URLCache(memoryCapacity: 4*1024*1024, diskCapacity: 20*1024*1024, diskPath: nil) URLCache.shared = cache }4. 单接口动态修改缓存策略(精准控权)
不同接口缓存需求不同,无需全局统一,可单独指定策略:
NSURL *url = [NSURL URLWithString:@"https://xxx.com/home/banner"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // 优先读取缓存,优化页面秒开 request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;5. 系统缓存核心踩坑点(项目高频问题)
POST 请求默认不缓存:NSURLCache 只默认缓存 GET 请求,POST 无论是否配置协议头,系统均不自动缓存
缓存容量不足自动清理:不手动扩容,默认缓存极小,频繁丢失缓存数据
无法精准控制过期时间:系统缓存完全服从服务端Header,客户端无法单独干预
无针对性清理:只能全局清空缓存,无法单接口、单业务清理
结论:系统缓存适合静态、稳定、低更新频率数据,动态业务数据、个性化数据必须用自定义本地缓存。
五、第三层:业务自定义本地缓存(复杂场景终极方案)
针对系统缓存的短板,项目中必须封装自定义本地缓存,支持:精准过期时间、单接口清理、离线缓存、个性化数据存储、脏数据过滤,完全脱离系统限制。
1. 自定义缓存核心优势
支持 POST/GET 所有请求缓存,不限制请求方式
客户端自定义过期时间,无需依赖服务端配置
支持单条缓存删除、批量清空、过期自动清理
适配用户个性化数据、登录态数据、动态业务接口
2. 极简缓存工具类实战(可直接复用)
基于沙盒+字典归档实现轻量缓存,满足90%业务需求,大型项目可替换为 YYCache。
// 缓存工具核心方法 @interface NetworkCacheManager : NSObject // 写入缓存(带过期时间) + (void)saveCacheData:(id)data key:(NSString *)key expireTime:(NSTimeInterval)time; // 读取缓存 + (id)getCacheDataWithKey:(NSString *)key; // 删除单条缓存 + (void)removeCacheWithKey:(NSString *)key; // 清空所有缓存 + (void)clearAllCache; @end核心逻辑:存储数据+时间戳,读取时判断是否过期,过期直接返回空并清理脏数据。
3. 实战场景适配
个人中心数据:缓存时长5分钟,兼顾实时性与加载速度
首页推荐数据:缓存时长10分钟,离线可查看,弱网秒开
本地配置、文案、图标:缓存时长24小时,减少重复请求
临时弹窗、活动数据:自定义短期缓存,活动结束自动失效
六、高阶实战:无感刷新架构(CacheThenNetwork 业界最优方案)
很多项目的痛点:要么纯读缓存不更新,要么纯实时加载白屏卡顿,无法兼顾「秒开体验」和「数据实时性」。
无感刷新(CacheThenNetwork)是一线大厂通用优化方案,核心逻辑:先加载本地缓存渲染UI(无白屏、秒开),同时后台请求最新数据,数据更新后静默刷新页面,用户无感知。
1. 无感刷新完整流程
页面初始化,优先读取本地缓存,立刻渲染页面,实现秒开
子线程发起网络请求,拉取服务端最新数据
对比新老数据,一致则不刷新,不一致静默更新UI并写入新缓存
全程无loading、无白屏、无闪烁,用户完全无感
2. 无感刷新实战代码(AFN+自定义缓存)
- (void)loadHomeData { NSString *cacheKey = @"home_page_data"; // 1. 优先读取缓存,秒开页面 id cacheData = [NetworkCacheManager getCacheDataWithKey:cacheKey]; if (cacheData) { [self renderUIWithData:cacheData]; } // 2. 后台静默请求最新数据 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; [manager GET:@"https://xxx.com/home/data" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { // 3. 对比数据,不一致则更新UI+缓存 if (![responseObject isEqualToDictionary:cacheData]) { [self renderUIWithData:responseObject]; [NetworkCacheManager saveCacheData:responseObject key:cacheKey expireTime:600]; } } failure:nil]; }3. 无感刷新适配场景与禁忌场景
✅ 适合无感刷新
首页、推荐页、资讯列表、静态文案
弱网、离线需要可用的页面
数据更新不频繁、对实时性要求不极致的页面
❌ 禁止无感刷新
支付、订单、钱包、隐私敏感数据
实时榜单、实时聊天、实时行情数据
需要强一致性的业务数据
七、项目落地:三层缓存组合最佳实践(规范可直接上线)
1. 静态资源/基础配置(HTTP强缓存+系统缓存)
图片、图标、静态文案、全局配置,服务端配置 Cache-Control 长期缓存,客户端默认走系统缓存,无需手动干预,极致提速。
2. 动态常规业务(协商缓存+自定义缓存)
首页、列表页,开启 HTTP 协商缓存,同时客户端增加短时自定义缓存,实现 304 复用+离线可用双重保障。
3. 高实时性业务(禁用缓存)
订单、支付、个人中心,设置ReloadIgnoringLocalCacheData强制实时请求,杜绝脏数据。
4. 离线可用页面(纯自定义缓存)
历史记录、本地内容页,使用自定义缓存永久存储,仅手动触发更新。
八、高频踩坑复盘(解决项目99%缓存BUG)
坑点1:POST 请求缓存不生效
原因:NSURLCache 系统缓存仅支持GET请求,POST 默认不缓存,协议头配置无效。
解决:POST 接口全部使用自定义本地缓存,手动读写存储。
坑点2:数据更新后,App 一直展示旧缓存
原因:强缓存未过期,客户端不发起校验请求;自定义缓存过期时间过长。
解决:动态调整 Cache-Control 时长,关键接口降低缓存有效期,客户端增加手动刷新、缓存清除逻辑。
坑点3:缓存堆积,App 体积越来越大
原因:系统缓存、自定义缓存无自动清理机制,过期数据持续堆积。
解决:初始化配置合理缓存容量,自定义缓存增加过期自动清理、版本更新清空旧缓存逻辑。
坑点4:304 请求依然返回完整数据
原因:服务端未正确配置 ETag/Last-Modified,客户端请求头未携带校验参数。
解决:前后端对齐协商缓存配置,确保校验头正常传递。
坑点5:无感刷新导致UI闪烁、数据错乱
原因:未做新老数据对比,每次请求都强制刷新UI。
解决:新增数据比对逻辑,仅数据变更时刷新页面。
九、面试高频必背问答
1. iOS 系统为什么 POST 请求不缓存?如何解决?
NSURLCache 系统机制默认只缓存 GET 接口,POST 视为动态请求不做缓存;解决方式是客户端封装自定义本地缓存,手动实现 POST 数据缓存逻辑。
2. 强缓存和协商缓存的区别?
强缓存:有效期内无需请求服务器,直接读本地缓存,速度最快;协商缓存:缓存过期后发起轻量校验,304复用缓存,200更新数据,兼顾速度与实时性。
3. NSURLCache 缓存机制是什么?
系统自动管理的内存+磁盘双缓存,默认遵循HTTP协议缓存策略,仅支持GET请求,可全局配置缓存容量,无法精准控制单接口过期时间与清理规则。
4. 什么是无感刷新?核心优势是什么?
CacheThenNetwork 策略,先读缓存渲染页面保证秒开体验,后台静默请求最新数据,变更后无感更新UI,解决了「白屏卡顿」和「数据滞后」的矛盾,是移动端最优加载方案。
5. 三层缓存如何选型?
静态资源用HTTP+系统缓存、常规动态页面用协商缓存+自定义缓存、高实时页面禁用缓存、离线页面纯自定义缓存。
十、全文总结
1.HTTP协议缓存:服务端主导,强缓存提速、协商缓存保更新,是基础缓存标准,无需客户端复杂编码。
2.NSURLSession系统缓存:NSURLCache 自动接管,双缓存机制,适配静态GET接口,存在POST不缓存、控权弱的短板。
3.自定义本地缓存:客户端完全可控,适配所有请求、精准控时、按需清理,解决系统缓存所有短板。
4.无感刷新架构:CacheThenNetwork 策略,平衡体验与实时性,是中大型项目页面加载的最优解决方案。
5.工程核心:三层缓存分层搭配、按需选型、规范落地,可彻底解决iOS项目所有缓存乱象,实现极速加载、离线可用、数据实时的优质体验。