问题 NSURLCache在iOS5上提供不一致的结果,看似随意


我刚刚花了很长时间在NSURLCache上大喊大叫,所以我提供了一些建议,希望别人可以避免我的不幸。

这一切都开始得相当合理。我的新应用程序项目仅针对iOS 5及更高版本,因此我认为我可以利用新的NSURLCache实现来满足我的所有Web缓存需求。我需要NSURLCache的自定义子类来处理一些特殊任务,但API似乎都有所帮助。快速阅读文档,我即将参加比赛:

[NSURLCache setSharedURLCache:[[MyCustomCache alloc] initWithMemoryCapacity:8 * 1024 * 1024 //8mb 
                                                                    diskCapacity:32 * 1024 * 1024 // 32mb 
                                                                        diskPath:@"webcache.db"]];

我认为8mb缓存可以启动,我会用更大的磁盘缓存来支持它,这样我们就可以在本地提供更多更大的图像。我连接其余的网络代码以使用NSURLConnection(实际上,我使用过 MKNetworkKit,但结果证明是无关紧要的),并期待我的缓存中的好东西。果然,所有应该缓存的请求都会尽职尽责地保存到缓存中,并且当从缓存中提供响应时,响应会尽快快速地返回。这是Penrance of Penzance的常规制作,在我的网络堆栈中飞来飞去。

除非有些东西没有加起来。可以从缓存提供的请求仍然通过网络传出。除非他们不是。 高速缓存是否实际用于提供请求似乎完全随机且间歇性。 我沮丧地撕掉了我的头发,并从字面上挖掘了所有试图弄清楚发生了什么的东西。我构建测试应用程序,在整个地方设置断点,撕掉数据包跟踪,读取互联网上提到NSURLCache的每个单词,试验缓存控制标题,注释掉代码,绕过我的子类,甚至通过艰苦地追踪为NSURLCache及其CFNetworking朋友组装,试图了解下面的神秘逻辑。我大量提高了我对ARM和Objective-C调用约定的知识,并对低级调试有了一些了解,但实际上无法弄清楚发生了什么。整个事情感觉更像是Iolanthe的 梦魇之歌 而不是海盗王的良性独裁统治,而我几乎将它扔掉了。

TL / DR版本:NSURLCache似乎正在工作,但即使它们可用,也会随机返回缓存的结果。 


10246
2017-07-16 03:57


起源

您是否尝试将缓存用于NSURLConnection,UIWebView或两者?几个月前,我在WWDC上与Safari开发人员讨论过UIWebView缓存。他说,有一种双层缓存。从我自己的调试测试中,我发现了这一点 +[NSURLCache sharedURLCache] 在检查操作系统级别的Safari缓存之前检查。但是,响应不会缓存在应用程序的共享缓存中,并且谁知道操作系统级缓存的行为方式。 - goldierox
有趣。我只关注NSURLConnection方面,因为我们的应用程序没有充分利用UIWebView。我确实记得读过应用程序没有使用操作系统级别的Safari缓存,但我相信情况并非如此,特别是对于UIWebView。 - Zach Lipton


答案:


最后,我尝试了所有我一直在摆弄的比特的不同排列。我将内存和磁盘缓存大小都设置为8mb。

瞧,所有的怪异都消失了!应该缓存的所有内容都会被保存。应该从缓存中获得的所有内容都可以在没有网络请求的情况下获得服务。

似乎iOS5中的NSURLCache实现仍然不完整。它确实使用了磁盘和内存缓存(与iOS4及更早版本不同,它只实现了内存缓存),但是当请求未命中时,它实际上并没有通过内存缓存传递到磁盘缓存。因此,它基本上是盲目的运气(好吧,盲目的运气受到所有其他网络和缓存使用的影响)是否给定的响应恰好在内存中或在正确的时刻。这对于减少设备上的闪存文件IO可能很有用,但如果你期望从类中理性行为,那就太疯狂了。

因此,在笑声和欢快的舞蹈中,我检查了我的两行修复并急速为酒吧,最终与SO分享这些知识(和一个Apple bug报告),希望没有其他人必须通过这个再次痛苦。

这个悲惨故事的寓意: 如果你尝试在iOS5上使用磁盘容量大于内存容量的NSURLCache,会发生奇怪和邪恶的事情。不要这样做。并避免制造魔法仙女的敌人。


9
2017-07-16 03:57



我很遗憾地说这对我不起作用。 - bogardon
它让我的事情变得更好,但即使在之后我仍然有些奇怪。看来NSURLCache存在一些严重的同步问题(?)。我已经提交了一个雷达,与此同时,将我的所有图像缓存移动到SDWebImage的一个非常黑的分支上​​(github.com/rs/SDWebImage),至少表现得有决定性。 - Zach Lipton


FWIW这是我的最终解决方案:

https://gist.github.com/3245415

它需要使用FMDB,但结果非常好。


2
2017-08-07 06:04



好方法。我想过尝试类似这样的东西,但是当它显然表现得如此不可预测时,我最终会自己对SQLite DB进行捣乱。我担心你的修复程序会出现同步问题,尽管这可能在实践中有所体现。正如我上面所说,我一直在使用NSCache支持的SDWebImage分支(请参阅pull请求)来处理图像缓存,并对结果非常满意。 - Zach Lipton