2010-12-27 32 views
12

オブジェクトの束のメモリ内キャッシュを保持する必要があるアプリケーションを作成していますが、それは手に入らないので、NSCacheを使用してすべて保存する予定です。それは私のために浄化などの世話をするように見えますが、それは素晴らしいです。NSCacheの内容をディスクに保存する

また、起動時にキャッシュを保持したいので、キャッシュデータをディスクに書き込む必要があります。 NSCacheの内容をplistなどに保存する簡単な方法はありますか?おそらく、NSCache以外のものを使ってこれを達成するより良い方法はありますか?

このアプリは、iPhoneになりますので、私は唯一のiOSで利用可能なクラス4+だけではなく、OS Xの

感謝をする必要があります!

答えて

13

私は オブジェクトの束の メモリ内キャッシュを維持する必要があるアプリケーションを書いているが、私は店にNSCache を使用して計画していますので、それは 手から取得していませんそれをすべて。私のためにパージングなどの世話をするように見える これは素晴らしいです。 私はまた、キャッシュの間に を残しておきたいので、 をキャッシュに書き込む必要があります。 NSCacheの内容を簡単に保存する方法はありますか をplistなどに保存しますか? NSCache以外のものを使用して、これを達成するにはより良い方法がありますか?

あなたはCoreDataが何をしているかを正確に説明しました。パージおよびプルーニング機能を備えたオブジェクトグラフの持続性

NSCacheは、持続性を支援するように設計されていません。

コアデータを代わりに使用することをplist形式に固執することをお勧めしましたが、概念上の違いはそれほどありません。

+0

作成したほぼすべてのデータベースアプリケーションでCore Dataを使用しましたが、これはこれに最適な方法ではないようです。私はAPIの結果を保存するためにキャッシュを使用しています。起動時に、また既に読み込まれたものをロードするときには、アプリを賢明に保ちます。コアデータのデータベースが手に入らずに成長していくことを心配しています。コアデータが「一時的な」データのキャッシュに最もよく使用されるようには見えません。私が必要とする永続性は、基本的に私が必要とするメモリ内キャッシング機能の第2の機能です。そのため、NSCacheに傾いていました。 –

+0

Yah - 私はあなたの謎を見ることができます。 NSCodingは実装が難しいことではありません。いつでもそのパスを辿ることができます。次に、永続的なバージョンのキャッシュを書き込み/更新するときに問題が発生します。私の経験では、すべての複雑さを伴う永続性ホイールの再発明を終える道を辿るのはかなり簡単です。もちろん、最も優れたアプリケーションは最初に出荷されるアプリケーションです。 ;) – bbum

+0

@CoryImdieke、私たちは現在同じ状況に直面しており、NSCacheを使用する予定です。ちょうどあなたが選択した解決方法が不思議ですか? – Koolala

9

使用TMCache(https://github.com/tumblr/TMCache)。 NSCacheに似ていますが、永続性とキャッシュパージがあります。 Tumblrチームによって作成されました。

+1

残念ながら、それはもはや積極的に維持されていません。/ – manicaesar

+0

ベストアンサーはCoreDataまたはRealmを使用しています。 Coredataが最も標準的で、Realmは簡単に学習できます。 –

0

コアデータを処理せず、単にキャッシュの内容をディスクに保存しない方が便利な場合があります。 NSKeyedArchiverUserDefaultsでこれを達成できます(私は下記のコード例で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関連するタイプは、それがSetタイプパラメータの要件だからHashableなければなりません。ここで

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) 
    } 
} 

次はヘルパークラスCacheCodingを使用してCacheためNSCodingを実装する必要が

  • CCacheに準拠タイプです。NSCoder.encode(forKey:)方法は、キーパラメータのStringを受け入れるためStringに変換すること
    • スウィフトCustomStringConvertibleプロトコル:
    • C.Key関連タイプに適合しなければなりません。バック符号化するときに使用されたNSCoderキーから復号化中に抽出する方法がないので、我々は[String]Set<Key>を変換しkeysキーでNSCoderにそれを格納する必要Set<Key>
  • から[String]を変換する
  • スウィフトExpressibleByStringLiteralプロトコルオブジェクト。しかし、キャッシュにkeysというエントリがあると、キャッシュキーをkeysキーと区別するために、プレフィックスキャッシュキーに_を付けることもあります。
  • C.Value関連するタイプは、キャッシュに格納された値からNSCodingのインスタンスを取得するためにNSCodingConvertibleプロトコルに準拠する必要があります:あなたはNSCodingインスタンスから値を取り戻す必要があるため

    protocol NSCodingConvertible { 
        associatedtype Coding: NSCoding 
    
        var coding: Coding { get } 
    } 
    
  • Value.CodingValueProviderプロトコルに準拠しています

    protocol ValueProvider { 
        associatedtype Value 
    
        var value: Value { get } 
    } 
    
  • C.Value.Coding.ValueC.Valueは同等でなければならないbecau私たちがNSCodingのインスタンスを取得する際の値は、デコード時にNSCodingから返される値と同じタイプのエンコードでなければなりません。

    protocol Builder { 
        associatedtype Value 
    
        init() 
    
        func build() -> Value 
    } 
    

次に者がNSCacheCacheプロトコルに準拠してみましょう:

  • CBBuilderプロトコルに準拠し、Cタイプのキャッシュ・インスタンスを作成するのに役立ちますタイプです。ここには問題があります。 NSCacheにはNSCoderと同じ問題があります。格納されたオブジェクトのキーを抽出する方法はありません。この問題を回避するには、3つの方法があります。

      キー Setを保持し、 NSCacheするのではなく、どこでもそれを使用します
    1. カスタムタイプとラップ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) 
          } 
      } 
      
    2. をあなたはまだ、その後どこかNSCacheを渡す必要がある場合あなたはObjective-Cの中で、上記と同じことをBetterCacheとして拡張しようとすることができます。

    3. 他のキャッシュ実装を使用してください。

    これでCacheプロトコルに準拠したタイプになっています。使用する準備が整いました。

    のは、我々はそのタイプのキャッシュとNSCodingに格納されますどのインスタンスタイプBookを定義してみましょう:

    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) 
        } 
    } 
    

    読みやすくするためのいくつかのtypealiases:

    typealias BookCache = BetterCache<StringKey, Book> 
    typealias BookCacheCoding = CacheCoding<BookCache, BookCacheBuilder> 
    

    そしてインスタンス化するために、私たちを助けるビルダーCache例:

    class BookCacheBuilder: Builder { 
        required init() { 
        } 
    
        func build() -> BookCache { 
         return BookCache() 
        } 
    } 
    

    テストは:

    let cacheKey = "Cache" 
    let bookKey: StringKey = "My Favorite Book" 
    
    func test() { 
        var cache = BookCache() 
        cache[bookKey] = Book(title: "Lord of the Rings") 
        let userDefaults = UserDefaults() 
    
        let data = NSKeyedArchiver.archivedData(withRootObject: BookCacheCoding(cache: cache)) 
        userDefaults.set(data, forKey: cacheKey) 
        userDefaults.synchronize() 
    
        if let data = userDefaults.data(forKey: cacheKey), 
         let cache = (NSKeyedUnarchiver.unarchiveObject(with: data) as? BookCacheCoding)?.cache, 
         let book = cache.value(forKey: bookKey) { 
         print(book.title) 
        } 
    } 
    
  • 0

    あなたはAwesomeCacheを試してみてください。その主な機能:スウィフト

  • に書き込ま

    • 例単一オブジェクトの満了のために最高のパフォーマンスとサポートのため

    をオンディスクキャッシュNSCacheによって支え

  • 上使用:

    do { 
        let cache = try Cache<NSString>(name: "awesomeCache") 
    
        cache["name"] = "Alex" 
        let name = cache["name"] 
        cache["name"] = nil 
    } catch _ { 
        print("Something went wrong :(") 
    } 
    
  • 関連する問題