Памятка в статическом классе Objective-C - PullRequest
1 голос
/ 12 января 2010

Скажем, у меня есть метод класса, подобный

+ (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
   //
}

Спасибо !!

Ответы [ 3 ]

5 голосов
/ 12 января 2010

Если вы хотите создать кэш один раз и проверить его, я обычно использую метод +initialize. Этот метод вызывается до первого сообщения, отправленного классу, поэтому кэш будет создан до того, как +function:: (что, кстати, является ужасным именем селектора) может быть вызвано. В этом случае я обычно объявляю переменную кэша в файле .m, но объявление ее в определении метода также может работать.


Редактировать: Добавление примера по запросу 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.
}

Очевидно, что если значение не существует в кэше, у вас должен быть некоторый код, который добавляет значение. Кроме того, я понятия не имею, как вы собираетесь объединить param1 и param2 в качестве ключа для кэша или как вы будете хранить значение. (Возможно, +[NSNumber numberWithDouble:] и -[NSNumber doubleValue]?) Вы должны убедиться, что понимаете поиск по словарю, прежде чем применять такую ​​стратегию.

2 голосов
/ 12 января 2010

Я использую что-то вроде следующего. В отличие от версии, опубликованной @Quinn Taylor, эта версия имеет следующие свойства:

  • Создает NSAutoreleasePool, чтобы гарантировать, что пул существует. Лучше всего предполагать худшее, когда имеешь дело с кодом запуска. Безвреден, если пул уже существует.
  • Создает cache ровно один раз:
    • Безопасный вызов +initialize несколько раз (может происходить через подклассы).
    • Многопоточный сейф. Независимо от того, сколько потоков одновременно вызывает +initialize одновременно, cache гарантированно будет создан только один раз. Поток, который «выигрывает» атомарный CAS, сохраняет cache, а потоки, «теряющие» autorelease свои попытки.

Если вы хотите быть крайне консервативным, вы можете добавить проверки утверждений, что и pool и initCache не NULL. Также обратите внимание, что это никак не гарантирует, что cache будет использоваться многопоточным безопасным способом после его создания.

#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;
}
0 голосов
/ 12 января 2010

В зависимости от того, что вы пытаетесь сделать, и от того, является ли проблема безопасности потоков, вы также можете рассмотреть одноэлементный класс, как в ответ на этот предыдущий вопрос .

...