我正在编写一个应用程序,需要保留一堆对象的内存缓存,但这不会失控,所以我打算使用NSCache来存储它。看起来它会照顾清除等对我而言,这太棒了。
我还想在启动之间保持缓存,所以我需要将缓存数据写入磁盘。有没有一种简单的方法可以将NSCache内容保存到plist或其他东西?是否有更好的方法可以使用NSCache之外的其他东西来实现这一目标?
此应用程序将在iPhone上,因此我只需要iOS 4+以上的类,而不仅仅是OS X.
谢谢!
答案 0 :(得分:13)
我正在编写一个需要保留的应用 一堆内存缓存 对象,但不会离开 所以我打算使用NSCache 存储所有。看起来会 照顾清洗等对我来说, 太棒了。 我也想保留缓存 在发布之间,所以我需要写 缓存数据到磁盘。有没有 保存NSCache内容的简便方法 到一个plist或什么?在那儿 也许更好的方法来实现这一目标 使用NSCache以外的东西?
您几乎只是描述了CoreData的功能;具有清除和修剪功能的对象图的持久性。
NSCache
并非旨在协助持久性。
鉴于您建议坚持使用plist格式,因此使用Core Data并不是一个很大的概念差异。
答案 1 :(得分:9)
使用TMCache(https://github.com/tumblr/TMCache)。它就像NSCache,但具有持久性和缓存清除功能。由Tumblr团队撰写。
答案 2 :(得分:4)
有时,不处理Core Data并将缓存内容保存到磁盘可能更方便。您可以使用NSKeyedArchiver
和UserDefaults
完成此操作(我在下面的代码示例中使用Swift 3.0.2)。
首先让NSCache
抽象出来,并想象我们希望能够保留符合协议的任何缓存:
protocol Cache {
associatedtype Key: Hashable
associatedtype Value
var keys: Set<Key> { get }
func set(value: Value, forKey key: Key)
func value(forKey key: Key) -> Value?
func removeValue(forKey key: Key)
}
extension Cache {
subscript(index: Key) -> Value? {
get {
return value(forKey: index)
}
set {
if let v = newValue {
set(value: v, forKey: index)
} else {
removeValue(forKey: index)
}
}
}
}
Key
关联类型必须为Hashable
,因为这需要Set
类型参数。
接下来,我们必须使用帮助程序类NSCoding
为Cache
实现CacheCoding
:
private let keysKey = "keys"
private let keyPrefix = "_"
class CacheCoding<C: Cache, CB: Builder>: NSObject, NSCoding
where
C.Key: CustomStringConvertible & ExpressibleByStringLiteral,
C.Key.StringLiteralType == String,
C.Value: NSCodingConvertible,
C.Value.Coding: ValueProvider,
C.Value.Coding.Value == C.Value,
CB.Value == C {
let cache: C
init(cache: C) {
self.cache = cache
}
required convenience init?(coder decoder: NSCoder) {
if let keys = decoder.decodeObject(forKey: keysKey) as? [String] {
var cache = CB().build()
for key in keys {
if let coding = decoder.decodeObject(forKey: keyPrefix + (key as String)) as? C.Value.Coding {
cache[C.Key(stringLiteral: key)] = coding.value
}
}
self.init(cache: cache)
} else {
return nil
}
}
func encode(with coder: NSCoder) {
for key in cache.keys {
if let value = cache[key] {
coder.encode(value.coding, forKey: keyPrefix + String(describing: key))
}
}
coder.encode(cache.keys.map({ String(describing: $0) }), forKey: keysKey)
}
}
下面:
C
是符合Cache
。C.Key
关联类型必须符合:
CustomStringConvertible
协议可转换为String
,因为NSCoder.encode(forKey:)
方法接受String
作为关键参数。 ExpressibleByStringLiteral
协议将[String]
转换回Set<Key>
Set<Key>
转换为[String]
并使用NSCoder
密钥将其存储到keys
,因为在NSCoder
密钥解码过程中无法提取编码对象时使用的。但是,当我们在密钥keys
的缓存中进入时,可能存在这样的情况,因此为了区分缓存密钥和特殊keys
密钥,我们使用_
为缓存密钥添加前缀。 C.Value
关联类型必须符合NSCodingConvertible
协议才能从缓存中存储的值中获取NSCoding
个实例:
protocol NSCodingConvertible {
associatedtype Coding: NSCoding
var coding: Coding { get }
}
Value.Coding
必须符合ValueProvider
协议,因为您需要从NSCoding
个实例中获取值:
protocol ValueProvider {
associatedtype Value
var value: Value { get }
}
C.Value.Coding.Value
和C.Value
必须相同,因为我们在编码时获得NSCoding
实例的值必须与我们从{{{{}获取的值具有相同的类型1}}解码时。
NSCoding
是一种符合CB
协议的类型,有助于创建Builder
类型的缓存实例:
C
接下来让我们protocol Builder {
associatedtype Value
init()
func build() -> Value
}
符合NSCache
协议。我们有一个问题。 Cache
与NSCache
具有相同的问题 - 它没有提供为存储对象提取密钥的方法。有三种方法可以解决这个问题:
使用自定义类型换取NSCoder
,其中包含密钥NSCache
并在任何地方使用它而不是Set
:
NSCache
如果你仍然需要在某个地方传递class BetterCache<K: AnyObject & Hashable, V: AnyObject>: Cache {
private let nsCache = NSCache<K, V>()
private(set) var keys = Set<K>()
func set(value: V, forKey key: K) {
keys.insert(key)
nsCache.setObject(value, forKey: key)
}
func value(forKey key: K) -> V? {
let value = nsCache.object(forKey: key)
if value == nil {
keys.remove(key)
}
return value
}
func removeValue(forKey key: K) {
return nsCache.removeObject(forKey: key)
}
}
,那么你可以尝试在Objective-C中扩展它,就像我上面用NSCache
做的那样。
使用其他一些缓存实现。
现在您的类型符合BetterCache
协议,您已准备好使用它。
让我们定义类型Cache
我们将在缓存中存储哪些实例,并为该类型定义Book
:
NSCoding
为了更好的可读性,使用了一些类型的术语:
class Book {
let title: String
init(title: String) {
self.title = title
}
}
class BookCoding: NSObject, NSCoding, ValueProvider {
let value: Book
required init(value: Book) {
self.value = value
}
required convenience init?(coder decoder: NSCoder) {
guard let title = decoder.decodeObject(forKey: "title") as? String else {
return nil
}
print("My Favorite Book")
self.init(value: Book(title: title))
}
func encode(with coder: NSCoder) {
coder.encode(value.title, forKey: "title")
}
}
extension Book: NSCodingConvertible {
var coding: BookCoding {
return BookCoding(value: self)
}
}
将帮助我们实例化typealias BookCache = BetterCache<StringKey, Book>
typealias BookCacheCoding = CacheCoding<BookCache, BookCacheBuilder>
实例的构建器:
Cache
测试它:
class BookCacheBuilder: Builder {
required init() {
}
func build() -> BookCache {
return BookCache()
}
}
答案 3 :(得分:0)
您应该尝试AwesomeCache。其主要特点:
示例:
do {
let cache = try Cache<NSString>(name: "awesomeCache")
cache["name"] = "Alex"
let name = cache["name"]
cache["name"] = nil
} catch _ {
print("Something went wrong :(")
}