Do you need help on a specific subject? Use the contact form (Request a blog entry) on the right hand side.

2015-04-02

Swift "Cache" design pattern

I have written about cached variables before, but since then I have done a little refining and have now established a standard approach to cached values that I would dare to call a design pattern.

It uses a generic for the cached values itself like this:

class Cached<T> {
    
    
    // The cache itself
    
    private var cached: T?
    
    
    // The closure that will provide the value when necessary
    
    private var function: (() -> T)?
    
    
    // Designated initializer
    
    init(function: () -> T ) {
        self.function = function
    }
    
    
    // A kludge, to allow closures to use "self".
    
    init() {}
    
    
    // Accessor
    
    func value() -> T {
        if cached == nil {
            cached = function!()
        }
        return cached!
    }
    
    
    // Reset of the cached value
    
    func invalidate() {
        cached = nil
    }

}

I use this cache inside my other classes as follows:

class Square {
    
    
    // The cached value depends on changes to this property
    
    var sidelength: Double = 0 {
        didSet {
            cachedArea.invalidate()
        }
    }
    
    
    // A getter for the cached property
    
    var area: Double {
        get {
            return cachedArea.value()
        }
    }

    
    // The initialiser must specify the closures
       used to calculate the cached values.
    
    init() {
        cachedArea = Cached(function: {
           [unowned self] in return self.calculateArea()
        } )
    }
    
    
    // The cached value itself is private to hide its true nature.
    
    private var cachedArea = Cached<Double>()


    // A function is used to calculate the cached value,
       this keeps the initialiser readable.
    
    private func calculateArea() -> Double {
        return sidelength * sidelength
    }
}

To me, this pattern keeps the implementation neat, readable and hides any complexity from the users of the class.

2015.05.04: Addendum, to invalidate a cache, always try to use property observers. I.e the 'didSet' and 'willSet' observers.

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

2 comments:

  1. Seems a little verbose.
    var sidelength: Double = 0 {
    didSet {
    _area = nil
    }
    }

    private var _area: Double? = nil
    var area: Double {
    get {
    return _area ?? areaValue()
    }
    }
    func areaValue() -> Double {
    let v = sidelength * sidelength
    _area = v
    return v
    }


    ReplyDelete
  2. That is certainly possible. At the time I needed more external control to invalidate/initialize the cache. Using an explicit type with invalidate operator made the code all that much more understandable.

    ReplyDelete