2010-12-27 50 views
12

Tôi đang viết một ứng dụng cần lưu bộ nhớ cache trong bộ nhớ của một loạt các đối tượng, nhưng điều đó không thoát ra khỏi tầm tay vì vậy tôi đang lập kế hoạch sử dụng NSCache để lưu trữ tất cả . Có vẻ như nó sẽ chăm sóc thanh trừng và như vậy đối với tôi, điều đó thật tuyệt vời.Lưu Nội dung NSCache vào Đĩa

Tôi cũng muốn duy trì bộ nhớ cache giữa các lần khởi chạy, vì vậy tôi cần ghi dữ liệu bộ nhớ cache vào đĩa. Có cách nào dễ dàng để lưu nội dung NSCache vào plist hay gì đó không? Có cách nào tốt hơn để thực hiện điều này bằng cách sử dụng một cái gì đó khác hơn là NSCache?

Ứng dụng này sẽ có trên iPhone, vì vậy tôi sẽ chỉ cần các lớp có sẵn trong iOS 4+ chứ không chỉ OS X.

Cảm ơn!

Trả lời

13

Tôi đang viết một ứng dụng mà cần phải giữ một bộ nhớ cache trong bộ nhớ của một loạt các đối tượng, nhưng điều đó không thoát khỏi tay vì vậy tôi đang lập kế hoạch về việc sử dụng NSCache để lưu trữ tất cả. Có vẻ như nó sẽ chăm sóc tẩy và như vậy đối với tôi, thật tuyệt vời. Tôi cũng muốn lưu bộ nhớ cache giữa các lần khởi chạy, vì vậy tôi cần ghi dữ liệu bộ nhớ cache vào đĩa . Có cách dễ dàng để lưu nội dung NSCache vào plist hay gì đó không? Có có thể là những cách tốt hơn để thực hiện việc này bằng cách sử dụng một cái gì đó khác ngoài NSCache?

Bạn đã mô tả chính xác những gì CoreData làm; sự bền bỉ của đồ thị đối tượng với khả năng tẩy và cắt tỉa.

NSCache không được thiết kế để hỗ trợ tính kiên trì.

Giả sử bạn đề xuất duy trì định dạng plist, sử dụng Dữ liệu cốt lõi thay vì không phải là sự khác biệt lớn về khái niệm.

+0

Tôi đã sử dụng Dữ liệu cốt lõi trên hầu hết mọi ứng dụng cơ sở dữ liệu tôi đã tạo, nhưng có vẻ như đây không phải là kết quả phù hợp nhất cho nó. Tôi đang sử dụng bộ nhớ cache để lưu trữ kết quả API, giữ cho ứng dụng bị zippy khi khởi chạy và khi tải những thứ đã được tải. Tôi lo lắng về cơ sở dữ liệu cơ sở dữ liệu cốt lõi và không thể phát triển. Nó chỉ có vẻ không phải là dữ liệu cốt lõi được sử dụng tốt nhất cho bộ nhớ đệm "tạm thời" dữ liệu. Sự kiên trì mà tôi cần về cơ bản là chức năng thứ cấp cho chức năng bộ nhớ đệm trong bộ nhớ mà tôi cần, đó là lý do tại sao tôi đang hướng tới NSCache. –

+0

Yah - Tôi có thể thấy câu hỏi hóc búa của bạn. NSCoding không phải là * mà * khó thực hiện - bạn luôn có thể đi xuống con đường đó. Sau đó, câu hỏi sẽ trở thành khi nào để viết/cập nhật phiên bản cache liên tục. Theo kinh nghiệm của tôi, nó là khá dễ dàng để đi xuống một con đường mà kết thúc lên tái phát minh ra bánh xe kiên trì với tất cả các phức tạp của nó. Và, tất nhiên, các ứng dụng thực hiện tốt nhất là một trong những tàu đầu tiên. ;) – bbum

+0

@CoryImdieke, chúng tôi đang đối mặt với tình huống tương tự hiện tại và tôi đang lên kế hoạch sử dụng NSCache. Chỉ cần tự hỏi bạn đã chọn giải pháp nào? – Koolala

9

sử dụng TMCache (https://github.com/tumblr/TMCache). Nó giống như NSCache nhưng với sự kiên trì và dọn dẹp bộ đệm ẩn. Được viết bởi nhóm Tumblr.

+1

Thật không may, nó không còn được duy trì tích cực:/ – manicaesar

+0

câu trả lời hay nhất hiện nay là sử dụng CoreData hoặc Realm. Coredata là tiêu chuẩn nhất, Realm dễ học hơn. –

0

Đôi khi có thể thuận tiện hơn là không xử lý Dữ liệu cốt lõi và chỉ để lưu nội dung bộ nhớ cache vào đĩa. Bạn có thể đạt được điều này với NSKeyedArchiverUserDefaults (Tôi đang sử dụng Swift 3.0.2 trong các ví dụ mã bên dưới).

trước tiên hãy trừu tượng từ NSCache và tưởng tượng rằng chúng tôi muốn để có thể tồn tại bất kỳ bộ nhớ cache mà phù hợp với giao thức:

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 liên quan đến loại có được Hashable bởi vì đó là yêu cầu đối với Set tham số kiểu.

Tiếp theo chúng ta phải thực hiện NSCoding cho Cache sử dụng lớp helper 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) 
    } 
} 

đây:

  • C là kiểu đó phù hợp với Cache.
  • C.Key liên quan đến loại có để phù hợp với:
    • Swift CustomStringConvertible giao thức được chuyển đổi thành StringNSCoder.encode(forKey:) phương pháp chấp nhận String cho thông số quan trọng.
    • Swift ExpressibleByStringLiteral giao thức để chuyển đổi [String] trở lại Set<Key>
  • Chúng tôi cần phải chuyển đổi Set<Key>-[String] và lưu nó để NSCoder với keys chính vì không có cách nào để trích xuất trong giải mã từ NSCoder phím đã được sử dụng khi mã hóa các đối tượng. Nhưng có thể có tình huống khi chúng tôi cũng có mục nhập trong bộ nhớ cache với khóa keys để phân biệt các khóa bộ nhớ cache từ khóa đặc biệt keys, chúng tôi tiền tố khóa bộ nhớ cache với _.
  • C.Value liên quan đến loại có để phù hợp với NSCodingConvertible giao thức để có được NSCoding trường từ các giá trị được lưu trữ trong bộ nhớ cache:

    protocol NSCodingConvertible { 
        associatedtype Coding: NSCoding 
    
        var coding: Coding { get } 
    } 
    
  • Value.Coding phải tuân theo các ValueProvider giao thức bởi vì bạn cần để có được giá trị trở lại từ NSCoding trường hợp:

    protocol ValueProvider { 
        associatedtype Value 
    
        var value: Value { get } 
    } 
    
  • C.Value.Coding.ValueC.Value phải tương đương becau se giá trị mà từ đó chúng tôi nhận được ví dụ NSCoding khi mã hóa phải có cùng loại với giá trị mà chúng tôi nhận được từ NSCoding khi giải mã.

  • CB là một loại mà phù hợp với Builder giao thức và giúp tạo ra ví dụ bộ nhớ cache của C loại:

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

Tiếp theo chúng ta hãy làm NSCache phù hợp với Cache giao thức. Ở đây chúng tôi có một vấn đề. NSCache có cùng vấn đề với tên NSCoder - nó không cung cấp cách trích xuất khóa cho các đối tượng được lưu trữ. Có ba cách để workaround này:

  1. Bọc NSCache với kiểu tùy chỉnh mà sẽ giữ phím Set và sử dụng nó ở khắp mọi nơi thay vì 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. Nếu bạn vẫn cần phải vượt qua NSCache ở đâu đó rồi bạn có thể thử mở rộng nó trong Objective-C làm điều tương tự như tôi đã làm ở trên với BetterCache.

  3. Sử dụng một số triển khai bộ nhớ cache khác.

Bây giờ bạn có loại phù hợp với giao thức Cache và bạn đã sẵn sàng để sử dụng.

Hãy xác định loại Book mà trường hợp chúng tôi sẽ lưu trữ trong bộ nhớ cache và NSCoding cho loại đó:

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

Một số typealiases để có thể đọc tốt hơn:

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

Và builder rằng sẽ giúp chúng ta nhanh chóng Cache ví dụ:

class BookCacheBuilder: Builder { 
    required init() { 
    } 

    func build() -> BookCache { 
     return BookCache() 
    } 
} 

Kiểm tra:

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

Bạn nên thử AwesomeCache. Các tính năng chính của nó:

  • viết bằng Swift
  • sử dụng trên trên đĩa bộ nhớ đệm
  • được hỗ trợ bởi NSCache cho hiệu suất tối đa và hỗ trợ cho hết hạn của các đối tượng đơn

Ví dụ:

do { 
    let cache = try Cache<NSString>(name: "awesomeCache") 

    cache["name"] = "Alex" 
    let name = cache["name"] 
    cache["name"] = nil 
} catch _ { 
    print("Something went wrong :(") 
} 
Các vấn đề liên quan