ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

iOS HTTP响应如何落盘?剖析SDURLCache的MD5缓存键与NSKeyedArchiver归档策略

2026/8/27 15:46:04 拓冰建站 浏览量
iOS HTTP响应如何落盘?剖析SDURLCache的MD5缓存键与NSKeyedArchiver归档策略 iOS HTTP响应如何落盘剖析SDURLCache的MD5缓存键与NSKeyedArchiver归档策略【免费下载链接】SDURLCacheURLCache subclass with on-disk cache support on iPhone/iPad. Forked for speed!项目地址: https://gitcode.com/gh_mirrors/sdu/SDURLCache一句话导读SDURLCache 是一个继承自NSURLCache的 iOS 网络缓存库它把 HTTP 响应真正写进 App 的磁盘目录让图片、接口数据在下次打开时秒开。本文带你拆解它的两大核心——用MD5 缓存键把 URL 变成稳定的磁盘文件名、用NSKeyedArchiver 归档策略把整个响应对象序列化成单个文件并顺带读懂它的过期判断与 LRU 淘汰逻辑。 适合人群想理解 iOS 磁盘缓存原理、或正在自研网络缓存层的 iOS 开发者。无需任何背景跟随本文即可看懂源码全貌。 为什么需要 SDURLCache——iOS 磁盘缓存的来龙去脉早期 iPhone OS 上苹果出于节省 Flash 寿命与磁盘空间的考虑悄悄移除了NSURLCache的磁盘缓存能力即使你传入NSURLCacheStorageAllowed系统也会一律按NSURLCacheStorageAllowedInMemoryOnly仅内存处理且无法强制开启。结果就是杀掉 App缓存全部丢失每次冷启动都要重新发请求。SDURLCache 通过继承NSURLCache并覆写读写逻辑把磁盘 HTTP 缓存这一能力还给开发者。它基于 ARC GCDGreen Dispatch重写相比原始版本速度更快、线程模型更干净。维度原生 NSURLCache早期 iOSSDURLCache内存缓存✅ 支持✅ 沿用父类磁盘缓存❌ 被系统禁用✅ 自己写盘淘汰策略仅内存LRU最近最少使用线程模型系统内部两条 GCD 串行队列 提示从 iOS 5 起NSURLCache已能正常按Cache-Control落盘。SDURLCache 的价值在于可控性——你可以指定磁盘路径、容量、最小缓存时长并精确干预哪些响应该被缓存。 快速上手三步给 App 装上磁盘缓存给 App 接入磁盘缓存只需三步建实例 → 指定容量 → 替换共享缓存。完整说明见 README.md。SDURLCache *urlCache [[SDURLCache alloc] initWithMemoryCapacity:1024 * 1024 // 1MB 内存缓存 diskCapacity:1024 * 1024 * 5 // 5MB 磁盘缓存 diskPath:[SDURLCache defaultCachePath]]; [NSURLCache setSharedURLCache:urlCache];默认缓存目录位于系统缓存区Library/Caches/SDNetworkingURLCache由 SDURLCache.m 的defaultCachePath决定不会被 iTunes 同步。 MD5 缓存键URL 如何变成磁盘文件名这是 SDURLCache 的第一个精妙设计。磁盘上不能直接用原始 URL 当文件名太长、含特殊字符、含#锚点它需要稳定、定长、碰撞概率极低的标识——MD5 正好满足。核心方法cacheKeyForURL:见 SDURLCache.m (NSString *)cacheKeyForURL:(NSURL *)url { const char *str [url.absoluteString UTF8String]; unsigned char r[CC_MD5_DIGEST_LENGTH]; CC_MD5(str, strlen(str), r); // CommonCrypto 计算 MD5 static NSString *cacheFormatVersion 2; return [NSString stringWithFormat:%_%02x%02x%02x%02x...%, // 32 位十六进制 cacheFormatVersion, r[0], r[1], /* ... r[15] */]; }三个设计点值得留意版本号前缀2_cacheFormatVersion是个常量。一旦归档格式升级只需把版本号从2改成3旧缓存键就全部对不上实现一次性清空历史缓存无需逐个删除。定长 32 位无论 URL 多长键都是固定长度文件系统友好排序与比较都极快。先规范化再哈希storeCachedResponse:/cachedResponseForRequest:都会先调用 canonicalRequestForRequest: 把 URL 中的#fragment片段截掉保证同一资源不同锚点落到同一个缓存文件。 记忆口诀规范化 URL → UTF-8 字节 → MD5 → 加版本前缀 → 文件名。 NSKeyedArchiver 归档策略一个对象一次写盘有了缓存键文件名接下来存什么SDURLCache 没有把响应头、响应体、用户信息分散成多个文件而是把整个NSCachedURLResponse对象一次性归档成单个二进制文件。第一步先给NSCachedURLResponse补上编解码能力NSCoder分类见 SDURLCache.m- (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:self.data forKey:data]; // 响应体 [coder encodeObject:self.response forKey:response]; // 响应头/状态码 [coder encodeObject:self.userInfo forKey:userInfo]; // 用户附加信息 [coder encodeInt:self.storagePolicy forKey:storagePolicy]; } - (id)initWithCoder:(NSCoder *)coder { return [self initWithResponse:[coder decodeObjectForKey:response] data:[coder decodeObjectForKey:data] userInfo:[coder decodeObjectForKey:userInfo] storagePolicy:[coder decodeIntForKey:storagePolicy]]; }第二步写盘与读盘各只有一行GCD 异步不阻塞主线程操作调用位置写盘archiveRootObject:toFile:SDURLCache.m读盘unarchiveObjectWithFile:SDURLCache.m归档进文件的字段含义dataHTTP 响应体图片 / JSON / 文本等原始字节responseNSHTTPURLResponse状态码 全部响应头userInfo业务侧自定义信息storagePolicy存储策略内存 / 磁盘⚡ 一次dispatch_async丢到disk IO队列完成写盘主 RunLoop 零卡顿。这就是标题里响应如何落盘的完整答案键是 MD5内容是 NSKeyedArchiver 序列化后的单个归档文件。读盘时还有个小技巧先在内存字典里查accesses缓存命中预判命中后才真正unarchiveObjectWithFile:用一次内存查询换掉一次文件 IO读到后还会顺手回填到内存缓存供后续请求复用。⏳ 过期判断这个 HTTP 响应到底该不该缓存并非所有响应都值得落盘。SDURLCache 在 expirationDateFromHeaders:withStatusCode: 里实现了一套按优先级递减的过期判断优先级信号处理1状态码不在白名单仅 200/203/300/301/302/307/410 可缓存不缓存2Pragma: no-cache或Cache-Control: no-store不缓存3Cache-Control: max-agennow n秒后过期4Expires头换算成本地过期时刻5仅有Last-Modified启发式缓存取文档年龄 × 10%见 SDURLCache.m6什么都没有默认缓存1 小时见 SDURLCache.m两个容易被忽略的细节有Etag就跳过过期检查RFC 2616 要求带 ETag 的响应必须用于条件请求304 重校验此时即使看起来过期也要保留交给NSURLConnection去协商。minCacheInterval门槛默认5 分钟见 SDURLCache.m 与 SDURLCache.h。如果某响应距过期不足 5 分钟干脆不写盘——还没读完就过期的东西交给内存缓存即可省得白耗磁盘与 IO。♻️ LRU 淘汰磁盘空间如何自动回收缓存容量有限超了怎么办SDURLCache 用**最近最少使用LRU**自动回收全程后台完成记账每个缓存键在cacheInfo.plist二进制 plist见 SDURLCache.m里登记accesses最后访问时间与sizes文件字节数两张表。定时巡检periodicMaintenanceSDURLCache.m由一个 5 秒的dispatch_source定时器驱动kAFURLCacheMaintenanceTime见 SDURLCache.m。超标即清理当_diskCacheUsage diskCapacity时触发balanceDiskUsageSDURLCache.m按accesses升序排列从最久未访问的开始删直到回到容量之内。 全程只用两条 GCD 串行队列disk cache管元数据、disk IO管文件读写见 SDURLCache.m 一个可重入同步函数所有磁盘写入都不落在主线程。 关键源码文件导读文件作用SDURLCache.h公共接口minCacheInterval、ignoreMemoryOnlyStoragePolicy、isCached:等可配置项SDURLCache.m全部实现MD5 键、归档读写、过期判断、LRU 淘汰README.md使用说明与快速上手http-date.r1Ragel 状态机源码生成高性能 HTTP 日期解析表SDURLCacheTests.mHTTP 日期解析 / 过期逻辑的单元测试LICENCEMIT 开源协议✅ 小结三句话记住 SDURLCacheMD5 缓存键 规范化 URL 2_版本前缀 32 位十六进制定长且可整体失效NSKeyedArchiver 归档 把data / response / userInfo / storagePolicy打包成单个文件写盘读盘各一行、GCD 异步不卡主线程过期 LRU 按Cache-Control → Expires → Last-Modified → 默认 1h判过期5 分钟以下的短期响应不写盘容量超标时按最近最少使用自动回收。读懂这套MD5 键 单对象归档 LRU 回收的组合拳你就能把任意网络库的磁盘缓存模块讲得明明白白。【免费下载链接】SDURLCacheURLCache subclass with on-disk cache support on iPhone/iPad. Forked for speed!项目地址: https://gitcode.com/gh_mirrors/sdu/SDURLCache创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考