Memoization in static Objective-C class
Say I have a class method like
+ (double)function:(id)param1 :(id)param2
{
// I want to memoize this like...
s开发者_开发知识库tatic NSMutableDictionary* cache = nil;
//
// test if (param1,param2) is in cache and return cached value, etc. etc
//
}
Thanks!!
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.
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 retainscache
, the threads that 'loose'autorelease
their attempts.
- Safe to call
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.
精彩评论