Question

Say I have a class method like

+ (double)function:(id)param1 :(id)param2
{
   // I want to memoize this like...
   static NSMutableDictionary* cache = nil;
   //
   // test if (param1,param2) is in cache and return cached value, etc. etc
   //
}

Thanks!!

Was it helpful?

Solution

If you want to create the cache once and check against it, I generally use an +initialize method. This method is called before the first message sent to the class, so the cache would be created before +function:: (which, by the way, is a terrible selector name) could be called. In this case, I usually declare the cache variable in the .m file, but declaring it in the method definition may also work.


Edit: Adding an example at request of OP:

// MyClass.m

static NSMutableDictionary* cache;

+ (void) initialize {
    cache = [[NSMutableDictionary alloc] init];
}

+ (double) cachedValueForParam1:(id)param1 param2:(id)param2 {
    // Test if (param1,param2) is in cache and return cached value.
}

Obviously, if a value doesn't exist in the cache, you should have some code that adds the value. Also, I have no idea how you intend to combine param1 and param2 as the key for the cache, or how you'll store the value. (Perhaps +[NSNumber numberWithDouble:] and -[NSNumber doubleValue]?) You'll want to make sure you understand dictionary lookups before implementing such a strategy.

OTHER TIPS

I use something like the following. Unlike the version posted by @Quinn Taylor, this version has the following properties:

  • Creates a NSAutoreleasePool to guarantee that a pool exists. Best to assume the worst when dealing with 'start-up' like code. Harmless if a pool already exists.
  • Creates cache exactly once:
    • Safe to call +initialize multiple times (may happen via sub-classing).
    • Multi-threaded safe. No matter how many threads concurrently call +initialize at the exact same time, cache is guaranteed to only be created once. The thread that 'wins' the atomic CAS retains cache, the threads that 'loose' autorelease their attempts.

If you want to be extremely conservative, you can add assertion checks that both pool and initCache are not NULL. Also note that this does nothing to ensure that cache is used in a multi-threaded safe way once it's been created.

#include <libkern/OSAtomic.h>

static NSMutableDictionary *cache;

+ (void)initialize
{
  NSAutoreleasePool   *pool      = [[NSAutoreleasePool alloc] init];
  NSMutableDictionary *initCache = [[[NSMutableDictionary alloc] init] autorelease];
  _Bool                didSwap   = false;

  while((cache == NULL) && ((didSwap = OSAtomicCompareAndSwapPtrBarrier(NULL, initCache, (void * volatile)&cache)) == false)) { /* Allows for spurious CAS failures. */ }
  if(didSwap == true) { [cache retain]; }

  [pool release];
  pool = NULL;
}

Depending on what you're trying to do and whether thread safety is an issue, you may also want to consider a singleton class as in the answer to this earlier question.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top