2010-01-11 2 views
1

Скажут, у меня есть метод класса, какзапоминания в статическом классе Objective-C

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

Спасибо !!

+0

дублируют http://stackoverflow.com/questions/554969/using-static-keyword-in-objective-c-when-defining-a-cached-variable – joshperry

ответ

5

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


Edit: Добавление пример по запросу 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]?) Перед реализацией такой стратегии вам нужно убедиться, что вы понимаете словарный поиск.

+0

Очевидно имена являются общими лол. Не могли бы вы привести пример создания переменной в + initialize, поскольку она не является переменной-членом экземпляра. – DevDevDev

+0

Я бы не рекомендовал этот подход. Это не многопоточное безопасное и безопасно вызывать '+ initialize' несколько раз (что может произойти через подклассы, вызывающие' [super initialize] '). – johne

+2

Конечно, есть способы, чтобы вы могли сделать метод «более безопасным», я просто не хотел излишне компрометировать ответ или подавить OP. И, честно говоря, это кеш, поэтому при замене существующего словаря было бы ошибкой и причиной утечек, было бы легко обойти такую ​​проблему. Например, мой '+ initialize' обычно проверяет (и часто синхронизирует)' [self class] 'перед тем, как делать что-либо. Лучшая практика замечательная, но давайте не будем бросать все возможные ошибки по простому вопросу, не так ли? –

0

В зависимости от того, что вы пытаетесь сделать, и является ли проблема безопасности потоков, вы также можете рассмотреть одноэлементный класс, как в the answer to this earlier question.

+0

Также ознакомьтесь с http://stackoverflow.com/questions/1986736/nsmutabledictionary-thread-safety по безопасности потоков NSMutableDictionary. – corprew

2

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

  • создает NSAutoreleasePool, чтобы гарантировать, что пул существует. Лучше всего предположить худшее при работе с «стартапом», как код. Безвреден, если пул уже существует.
  • Создает cache ровно один раз:
    • Safe для вызова +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; 
} 
+1

+1 Это хорошие проверки и те, что я делаю в своем собственном коде. Я оставил их для простоты, но они являются логическим продолжением. Пул авторесурсов - это подделка для меня, поскольку я всегда включаюсь в 'main()', но может быть применим, если его код используется третьими лицами. Для подклассов я обычно защищаю '-isMemberOfClass:', а для многопоточности я использую @synchronized блоки на '[self class]' для простоты. Тем не менее, хорошо включить в полный ответ. –

Смежные вопросы