0

I'm trying to understand the concept of NSCache, and one thing that strikes me is that a NSCache instance does not guarantee to give back the value to a key you stored before. It might not even store the key value pair when you try to add it, if it deems that the performance is more important at the moment.

What that implies, for me, is that:

  1. Each key must 'hold' enough information to generate the value if necessary
  2. Each query for the NSCache, which essentially is just in the form of a key, should thus wrap up all the information needed to generate the corresponding value.
  3. From the above two points one can say that NSCache serves no purpose of establishing any kind of association between a key and a value - the user must be able to generate the value independent of the cache, and the sole purpose of using a NSCache is not to 'look up' some value, but rather just to trade memory for some performance boost

So my problem is about storing transparency masks for images. Initially I thought I just need to use the names of the images as the keys, but from my deductions above it seems that's not sufficient - I also have to include all other parameters used in generating a mask e.g. the transparency threshold, for example. It also means that every time I ask the cache for a mask I have to provide ALL the parameters. And the only way that I can think of about doing that is to use something like NSInvocation as the key; but that seems a rather clunky solution.

L__
  • 866
  • 1
  • 9
  • 15
  • "the sole purpose of using a NSCache is not to 'look up' some value, but rather just to trade memory for some performance boost." That's what caches do. – JAB Jun 25 '13 at 17:26
  • Basically, if all you want is key-value association, just use `NSMutableDictionary` with the image names as the keys. http://stackoverflow.com/a/5756162/138772 – JAB Jun 25 '13 at 17:42

2 Answers2

2

It is the very nature of a cache to be volatile, so caches should only ever be used to speed up access to information that could also be acquired some other way.

Your idea to create keys that hold all this information should work - just remember to store all your keys somewhere other than the cache as well.

As for the key, you can create a very simple class that has nothing but a couple of properties (the ones that make up a key), an isEqual: and hash method and maybe an initializer that takes parameters for each of your properties.

This requires extremely little code, since accessors and iVars for properties are autogenerated, so the only thing you really need to write is the isEqual: method (and hash).

This class is so small and taylor-made for the particular case you need it for, it makes sense to declare and implement it at the top of the .m file you're going to use it in. This way, you don't pollute the rest of the system. Just add @interface and @implementation sections for your class at the top of your .m file.

fzwo
  • 9,842
  • 3
  • 37
  • 57
  • *cough* it's `isEqual:`, not `equals:`. I'd also be very surprised if a working `hash` isn't required though I can't find an explicit comment right now. – Tommy Jun 25 '13 at 17:31
  • 1
    I should really stop just quickly writing answers when I'm in a hurry. Thanks, corrected. Most important rule for the hash method: if two objects are equal, their hashes must be the same. Theoretically, having *all* objects return the same hash is legal, but will lead to very poor performance for lookups in dictionaries, sets, caches, etc. – fzwo Jun 25 '13 at 21:55
0

After more thought about this I think I've got one thing wrong - the keys in a NSCache do not necessarily need to hold all the information for generating the values. A key in a NSCache can serve the same purpose as that in a NSDictionary - a unique identifier to look up the value. The only difference, though, is that you'd always need to have a backup plan B for a NSCache in case the key-value pair added before is destroyed.

In simplier terms, operations on the two different classes look like this:

NSDictionary

  1. generate each value V for each key K and add the pairs to the dictionary
  2. look up V using K

NSCache

  1. look up V using K
  2. if V == nil, generate the value V and add the pair to the cache

Therefore it's possible to convert almost any NSDictionary to a NSCache, only that after the conversion you can't pass the NSCache around - you have to know how to generate the values at all times and thus the NSCache instance would most probably be a private property used exclusively in a certain class.

For my problem I've resolved to use a method like this (self is supposedly pointing to a subclass of NSCache, but I haven't tested it yet)

- (Mask *) maskForImageName:(NSString *)name maskGenerator:(Mask *(^)(NSString *))generator {
    Mask *mask = [self objectForKey:name];
    if (!mask) {
        mask = generator(name);
        [self setObject:mask forKey:name];
    }
    return mask;
}

It would be further simplified if objective-c is a functional, lazy-style language, in which case I don't even need to wrap the generator in a block; but I'm satisfied with this solution for now. In fact I feel that this pattern is almost always used with NSCache so I'd just add it as a category to NSCache.

L__
  • 866
  • 1
  • 9
  • 15