Как реализовать NSCache на основе времени с помощью setObject Swift 2.0

У меня есть NSDictionary, который я кэшировал. Мне нужно реализовать setObject на основе времени с отметкой времени. NSCache Class не имеет setExpiry. Любая помощь будет оценена по достоинству.

Это расширение, которое у меня есть до сих пор:

import Foundation

extension NSCache {

class var sharedInstance : NSCache {
    struct Static {
        static let instance : NSCache = NSCache()
    }
    return Static.instance
 }
}

Я нашел NSCache Extension на странице http://nshipster.com/nscache/ . Любой простой способ реализовать с отметкой времени истечения срока действия?

extension NSCache {
subscript(key: AnyObject) -> AnyObject? {
    get {
        return objectForKey(key)
    }
    set {
        if let value: AnyObject = newValue {
            setObject(value, forKey: key)
        } else {
            removeObjectForKey(key)
        }
    }
 }
}

person Tal Zion    schedule 08.11.2015    source источник
comment
Немного не по теме, но если вы пытаетесь создать синглтон из того, что у вас есть в расширении прямо сейчас, вы должны проверить [эту статью] (krakendev.io/blog/the-right-way-to-написать-одиночку). Вы можете создать синглтон в одной строке, например: static let sharedInstace = NSCache().   -  person Dominik Hadl    schedule 08.11.2015
comment
У меня есть JSON, который мне нужно кэшировать с отметкой времени истечения срока действия 1HR. Как вы думаете, мой подход достаточен? Спасибо..   -  person Tal Zion    schedule 08.11.2015


Ответы (2)


Вы также можете использовать таймер для очистки очереди:

private let ExpiringCacheObjectKey = "..."
private let ExpiringCacheDefaultTimeout: NSTimeInterval = 60

class ExpiringCache : NSCache {

    /// Add item to queue and manually set timeout
    ///
    /// - parameter obj: Object to be saved
    /// - parameter key: Key of object to be saved
    /// - parameter timeout: In how many seconds should the item be removed

    func setObject(obj: AnyObject, forKey key: AnyObject, timeout: NSTimeInterval) {
        super.setObject(obj, forKey: key)
        NSTimer.scheduledTimerWithTimeInterval(timeout, target: self, selector: "timerExpires:", userInfo: [ExpiringCacheObjectKey : key], repeats: false)
    }

    // Override default `setObject` to use some default timeout interval

    override func setObject(obj: AnyObject, forKey key: AnyObject) {
        setObject(obj, forKey: key, timeout: ExpiringCacheDefaultTimeout)
    }

    // method to remove item from cache

    func timerExpires(timer: NSTimer) {
        removeObjectForKey(timer.userInfo![ExpiringCacheObjectKey] as! String)
    }
}
person Rob    schedule 08.11.2015
comment
Привет @Rob, я получаю потенциальную ошибку, не могли бы вы помочь? [Скриншот] (dropbox. com/s/d9wocj6dzex380j/) Это то, что я думаю, должен быть синтаксис removeObjectForKey(timer.userInfo![ExpiringCacheObjectKey]) ? - person Tal Zion; 08.11.2015
comment
Не добавляйте таймер для каждого ключа в вашем кеше, вы должны добавить к ключу срок действия и проверять его при извлечении объекта. - person Ruud Visser; 29.04.2016
comment
Но если вы не извлечете объект, кеш не будет очищен, как хотел OP. Однако вы могли бы иметь один повторяющийся таймер, который перебирает все объекты в кеше и определяет, какие из них нужно очистить, если таковые имеются. Кроме того, если вы делаете что-то подобное, я бы выбрал универсальный, который фиксирует время последнего извлечения, а не встраивает его в ключ. Честно говоря, я думаю, что вся идея очистки, основанная на времени, чрезмерно продумана, и я бы просто придерживался countLimit и totalCostLimit. - person Rob; 29.04.2016

Вот основной подход.

PS: я не тестировал этот код и написал его в текстовом редакторе. Это может потребовать некоторых настроек в зависимости от ваших требований :)

import Foundation

protocol Cacheable: class {
    var expiresAt : NSDate { get set }
}

class CacheableItem : Cacheable {
    var expiresAt = NSDate()
}

extension NSCache {

    subscript(key: AnyObject) -> Cacheable? {
        get {
            if let obj = objectForKey(key) as? Cacheable {
                 var now = NSDate();
                if  now.isGreaterThanDate(obj.expiresAt) {
                    removeObjectForKey(key)
                }
            }

            return objectForKey(key) as? Cacheable
        }
        set {
            if let value = newValue {
                setObject(value, forKey: key)
            } else {
                removeObjectForKey(key)
            }
        }
    }
}

extension NSDate
{
    func isGreaterThanDate(dateToCompare : NSDate) -> Bool
    {
        var isGreater = false

        if self.compare(dateToCompare) == NSComparisonResult.OrderedDescending {
            isGreater = true
        }

        return isGreater
    }
}

Основано на ответе на переполнение стека.

person ProblemSlover    schedule 08.11.2015
comment
Большое спасибо! Проверим и посмотрим, как работает .. Ура :) - person Tal Zion; 08.11.2015
comment
@TalZion Я только что отредактировал ответ, поэтому код действительно компилируется. - person Dominik Hadl; 08.11.2015
comment
@TalZion Я отредактировал свой ответ. исправить логическую ошибку в сравнении дат. Надеюсь, это сработает для вас :) - person ProblemSlover; 08.11.2015
comment
Большое спасибо за вашу помощь :)!. Я пытался протестировать его, как я буду работать с ним? Когда я устанавливаю Object, как мне добавить отметку времени истечения срока действия? - person Tal Zion; 08.11.2015
comment
Вы можете назначить дату истечения срока действия перед добавлением в кеш, например var myCacheableItem = new CacheableItem() => myCacheableItem.expiresAt = myCacheableItem.expiresAt.dateByAddingTimeInterval(3600.0) => cache.set(значение: myCacheableItem, ключ: image.png) - person ProblemSlover; 08.11.2015