YMCache
YMCache 是一个轻量级对象缓存解决方案,适用于 iOS 和 macOS,专为高度并行的访问场景设计。YMCache 通过模拟 NSMutableDictionary
提供一个熟悉的应用程序接口,同时在内部利用 Apple 的 Grand Central Dispatch 技术来实现性能和一致性的平衡。
YahooFinance iOS 团队使用 YMCache 来多路复用对其成千上万的实时股票数据的访问,这些股票以不可预测的方式变化,变化速度也不可预测。YMCache 通过提供一系列易于理解的 读/写锁 访问语义,帮助减轻对中央数据存储的线程访问复杂性。
并行访问
- 所有读操作总是在调用线程上同步执行,但在所有读者间并发执行(在系统资源允许的情况下)。
- 所有写操作会异步执行
- 在写操作之后引发的读操作将等待写操作完成
上述规则允许有多个读者和一个写者。这种方法的美好结果是,读操作与写操作是串行的,强制执行一个合理的顺序:您可以确信预期的数据已完整写入。
功能
- 持久性:将缓存保存/加载到磁盘一次,或在定义的间隔内
- 淘汰策略:处理低内存情况,使用符合你需求的任何逻辑
- 序列化:任意模型转换免费。您可以使用 Mantle、直接的 NSJSONSerialization 或任何其他您能想到的格式!
- 批量操作:高效的多值读写。 (批量操作遵循 并行访问规则,但算作单个操作)
配置
我们支持通过 CocoaPods 和 Swift 包管理器 进行分发。
CocoaPods
-
将 YMCache 添加到项目的
Podfile
target :MyApp do pod 'YMCache', '~> 1.0' end
-
在项目目录中运行
pod update
或pod install
SwiftPM
将 .package(url: "https://github.com/yahoo/YMCache.git", from: "2.2.0")
添加到您的 package.swift
使用
摘要
YMMemoryCache *cache = [YMMemoryCache memoryCacheWithName:@"my-object-cache"];
cache[@"Key1"] = valueA;
MyVal *valueA = cache[@"Key1"];
[cache addEntriesFromDictionary:@{ @"Key2": value2 }];
NSDictionary *allItems = [cache allItems];
[cache removeAllObjects];
// cache = empty; allItems = @{ @"Key1": value1, @"Key2": value2 }
此缓存本质上是一个完全线程安全的 NSDictionary,具有读写顺序保证。
驱逐
手动驱逐
// Create memory cache with an eviction decider block, which will be triggered for each item in the cache whenever
// you call `-purgeEvictableItems:`.
YMMemoryCache *cache = [YMMemoryCache memoryCacheWithName:@"my-object-cache"
evictionDecider:^(NSString *key, NewsStory *value, void *context) {
return value.publishDate > [NSDate dateWithTimeIntervalSinceNow:-300];
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[cache purgeEvictableItems:nil];
});
此示例缓存包括一个驱逐块,该块在10秒延迟后被调用一次。您负责实现决策逻辑以确定哪些条目可以安全地被驱逐。在这种情况下,超过5分钟未发布的NewsStory
模型将从缓存中清除。在这种情况下,驱逐决策将在主队列上调用,因为这是从哪里调用-purgeEvictableItems:
的地方。
基于时间的驱逐
YMMemoryCache *cache = [YMMemoryCache memoryCacheWithName:@"my-object-cache"
evictionDecider:^(NSString *key, NewsStory *value, void *context) {
return value.publishDate > [NSDate dateWithTimeIntervalSinceNow:-300];
}];
cache.evictionInterval = 60.0; // trigger eviction every 60 seconds
这将在每60秒创建一个具有基于时间的缓存驱逐的缓存。请注意,驱逐决策者的自动调用将在一个任意背景线程上执行。此方法可以与其他手动驱逐调用相结合,以提供一种情况,其中缓存驱逐是按需触发的,但至少每隔N分钟。
低内存状态下的自动驱逐
// Create memory cache with an eviction decider block, which will be triggered for each item in the cache whenever
// you call `-purgeEvictableItems:`.
YMMemoryCache *cache = [YMMemoryCache memoryCacheWithName:@"my-object-cache"
evictionDecider:^(NSString *key, NewsStory *value, void *context) {
return value.publishDate > [NSDate dateWithTimeIntervalSinceNow:-300];
}];
// Trigger in-band cache eviction during low memory events.
[[NSNotificationCenter defaultCenter] addObserver:cache
selector:@selector(purgeEvictableItems:)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
// or, more commonly
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Trigger immediate synchronous cache cleanup
[self.cache purgeEvictableItems:nil];
}
响应低内存情况的驱逐决策块将在主线程上执行,因为只有主线程会发送低内存通知或调用-didReceiveMemoryWarning
。
观察变化
YMMemoryCache *cache = [YMMemoryCache memoryCacheWithName:@"my-object-cache"];
cache.notificationInterval = 0.5;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(cacheUpdated:)
name:kYFCacheDidChangeNotification
object:cache];
// from any thread, such as a network client on a background thread
cache[@"Key"] = value;
// within 0.5s (as per configuration) a notification will fire and call this:
- (void)cacheUpdated:(NSNotification *)notification {
// Get a snapshot of all values that were added or replaced since the last notification
NSDictionary *addedOrUpdated = notification.userInfo[kYFCacheUpdatedItemsUserInfoKey];
// Get a set of all keys that were removed since the last notification
NSSet *removedKeys = notification.userInfo[kYFCacheRemovedItemsUserInfoKey];
}
文件加密(iOS)和写入选项
YMCachePersistenceManager
使用 NSData
读取和写入磁盘上的数据。默认情况下,我们会以原子方式写入。您可以通过设置持久管理器的 fileWritingOptions
属性来控制写入选项,在下一个写入之前。
示例
要运行示例项目,请克隆存储库,然后从 Example 目录中的某个目录运行 pod install
。
示例:Mantle 序列化
使用 Mantle(版本 1 或 2)将缓存序列化为磁盘非常简单!查看 Examples/Mantle 中的预构建、现成的示例。
支持和贡献
报告任何错误或发送功能请求到 GitHub 问题。欢迎贡献拉取请求。有关详细信息,请参阅 CONTRIBUTING。
许可证
MIT 许可证。有关详细信息,请参阅 LICENSE 文件。