测试已测试 | ✓ |
语言语言 | Obj-CObjective C |
许可证 | Apache 2 |
发布最后发布 | 2016年11月 |
由 Hector Zarate,Aron Cedercrantz,Will Sackfield,John Sundell 维护。
每个人在应用生命周期中的某个时刻都会尝试实现缓存,这是我们提供的实现。这是一个库,允许人们使用具有存活时间(TTL)值和磁盘管理语义缓存 NSData
。
SPTPersistentCache
被设计为 LRU 缓存,它使用文件系统来存储文件,并在每个文件中插入缓存标题。这个缓存标题允许我们跟踪 TTL、最后更新时间、冗余检查等。这使得缓存能够知道文件被访问的频率、何时创建的、是否已损坏,以及允许做出有关缓存是否过期的决定。
使用不同的文件而不是单个二进制文件允许在不复杂的阻塞逻辑的情况下对缓存中的不同文件进行多次读取/写入。每个文件中的缓存标题可以很容易地删除,这在用于在 OS X 上运行的 SPTPersistentCacheViewer
工具中可以看到。
同时还包括规划垃圾回收的目的缓存,这允许用户确保他们不会为常用缓存数据(如图像)使用过多的空间。
SPTPersistentCache
可以用多种方式安装,包括传统的静态库和动态框架。
只需将 SPTPersistentCache.xcodeproj
包含在您的应用程序的 Xcode 项目中,然后在“构建阶段”部分将应用程序与库链接。
有关此框架使用示例,请参阅 SPTPersistentCache.xcworkspace
中的示例应用程序 SPTPersistentCacheDemo
。
最好为不同的数据类型使用不同的缓存,而不是只为整个应用程序使用一个大缓存。然而,只为每个缓存创建一个 SPTPersistentCache
实例,否则当两个不同的缓存最终写入同一文件时,可能会遇到异常。
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject stringByAppendingString:@"com.spotify.demo.image.cache"];
SPTPersistentCacheOptions *options = [SPTPersistentCacheOptions new];
options.cachePath = cachePath;
options.cacheIdentifier = @"com.spotify.demo.image.cache";
options.defaultExpirationPeriod = 60 * 60 * 24 * 30; // 30 days
options.garbageCollectionInterval = (NSUInteger)(1.5 * SPTPersistentCacheDefaultGCIntervalSec);
options.sizeConstraintBytes = 1024 * 1024 * 10; // 10 MiB
options.debugOutput = ^(NSString *string) {
NSLog(@"%@", string);
};
SPTPersistentCache *cache = [[SPTPersistentCache alloc] initWithOptions:options];
在将数据存储到SPTPersistentCache
中时,您必须了解文件系统的语义。键将用作缓存目录中的文件名用于保存。我们没有在底层实现哈希函数的原因是,我们希望用户可以选择使用哪种哈希函数,因此建议在将数据插入到缓存的键时,使用您自己的哈希函数创建键(在Spotify,我们使用SHA1,尽管现在存在更好的哈希函数)。如果您希望缓存记录(即文件)无限期存在,确保将其作为锁定的文件存储。
NSData *data = UIImagePNGRepresentation([UIImage imageNamed:@"my-image"]);
NSString *key = @"MyHashValue";
[self.cache storeData:data
forKey:key
locked:YES
withCallback:^(SPTPersistentCacheResponse *cacheResponse) {
NSLog(@"cacheResponse = %@", cacheResponse);
} onQueue:dispatch_get_main_queue()];
要恢复您之前在SPTPersistentCache
中已保存的数据,您只需提供用于存储数据的相同键即可。
NSString *key = @"MyHashValue";
[self.cache loadDataForKey:key withCallback:^(SPTPersistentCacheResponse *cacheResponse) {
UIImage *image = [UIImage imageWithData:cacheResponse.record.data];
} onQueue:dispatch_get_main_queue()];
注意,如果TTL已过期,您将不会收到结果。
有时,您可能需要在文件的TTL过期很久之后将其锁定在SPTPersistentCache
中。我们将在Spotify做的这样的事情的一个例子是将离线歌曲的封面艺术图像相关图片锁定(我们不想在您度假时使这些图像失效)。在锁定一个文件后,你必须确保最终解锁它,否则它会永远存在。锁定基本上是缓存与它的消费者之间的一个协议,即数据将保持在缓存中,直到它被明确解锁。
NSString *key = @"MyHashValue";
[self.cache lockDataForKeys:@[key] callback:nil queue:nil];
// Now my data is safe within the arms of the cache
[self.cache unlockDataForKeys:@[key] callback:nil queue:nil];
// Now my data will be subject to its original TTL
注意:如果您的缓存超出大小限制,即使是锁定文件也可能被剪枝。
SPTPersistentCache
中的垃圾回收功能不是自动运行的,您必须使用scheduleGarbageCollector
手动调用它。
[self.cache scheduleGarbageCollection];
这将按照SPTPersistentCacheOptions
类中提供的间隔安排垃圾回收,默认情况下这是外部的SPTPersistentCacheDefaultExpirationTimeSec
变量。要取消计划垃圾回收器,只需调用相反的功能unscheduleGarbageCollector
。
[self.cache unscheduleGarbageCollection];
有时您可能希望提前清空已计划执行的垃圾回收,擦除缓存或简单地无选择地删除所有锁定/未锁定文件。为了支持这些情况,我们提供了一些方法来执行这些操作。
// Lets wipe all the files we haven't explicitly locked
[self.cache wipeUnlockedFiles];
NSLog(@"Size = %@", @(self.cache.totalUsedSizeInBytes));
// Now let's wipe all the files we have explicitly locked
[self.cache wipeLockedFiles];
NSLog(@"Size = %@", @(self.cache.totalUsedSizeInBytes));
// Why not just wipe the entire cache?
[self.cache prune];
NSLog(@"Size = %@", @(self.cache.totalUsedSizeInBytes));
在Spotify,我们开始以统一的方式处理图像,并在这个过程中最初创建了一个处理图像及其缓存的组件。但后来我们的需求发生了变化,并且我们开始需要缓存我们的后台调用和预览MP3下载。通过这样做,我们成功地将我们的缓存逻辑分离成一个通用的组件,可以用于任何数据。
因此,我们总结了我们在缓存中所需的内容,关键特性是对特定数据的时间到(get TTL)、确保我们不会使用太多磁盘管理以及保护数据不被破坏。也将不同的缓存的分离成不同的文件非常有用(例如图像和mp3s),以便轻松测量每个项目所占用的空间。
拥有一个不错的GUI工具来检查SPTPersistentCache
目录的内容会很棒,所以我们做了一个。在这个仓库中,我们有一个名为SPTPersistentCacheViewer.xcodeproj
的项目,它是SPTPersistentCache.xcworkspace
的一部分。当你打开它并为OS X构建时,你会看到一个GUI,允许你检查缓存的内容,包括单个项的TTL和有效负载大小。
欢迎贡献,查看CONTRIBUTING.md文档获取更多信息。
本项目的许可协议为