+ (void) initialize not called (Objective C)

Мой метод + (void) инициализирован не вызывается, и я очень новичок в Objective C. Код находится в книге iPhone Game Development, и я должен вызвать метод явно для работы. Код в файле .m - это:

ResourceManager *g_ResManager;

@implementation ResourceManager

//initialize is called automatically before the class gets any other message, per from http://stackoverflow.com/info/145154/what-does-your-objective-c-singleton-look-like
+ (void) initialize
{
    static BOOL initialized = NO;
    if(!initialized)
    {
        initialized = YES;
        g_ResManager = [[ResourceManager alloc] init];
    }
}

...

@end

Но в файле .h выполняется внешнее объявление переменной:

extern ResourceManager *g_ResManager; //paul <3 camel caps, hungarian notation, and underscores.

@interface ResourceManager : NSObject {
   ...
}
...
@end

Я пробовал все (удалить внешний, поставить статику в объявлении .m) и всегда получать ошибки компиляции. Вышеприведенный код компилируется, но инициализация метода никогда не вызывается (поместите точку останова, чтобы увидеть это).

Некоторые подсказки?

Ответ 1

+initialize не вызывается, пока вы не отправите какое-либо сообщение экземпляру класса. Вы отправили сообщение?

Одна из возможных проблем может заключаться в том, что вы отправили сообщение в g_ResManager из другой части вашего кода? Это не сработает, потому что:

  • g_ResManager равен нулю во время запуска.
  • Вы отправляете сообщение g_ResManager, которое nil.
  • Что означает Objective-C время выполнения, как "отправка сообщения в класс", это не то, что он выглядит синтаксически в исходном коде, а реальный объект и отправленное сообщение.
  • Итак, в этом случае nil получает сообщение, nil не является экземпляром ResourceManager, поэтому +initialize также не вызывается.

Я бы изменил ваш код следующим образом: во-первых, в .m,

static ResourceManager *g_ResManager;

@implementation ResourceManager

//initialize is called automatically before the class gets any other message
+ (void) initialize
{
    static BOOL initialized = NO;
    if(!initialized)
    {
        initialized = YES;
        g_ResManager = [[ResourceManager alloc] init];
    }
}
+(ResourceManager*)sharedResourceManager
{
     return g_ResManager;
}
...

@end

а затем в .h, я бы просто имел

@interface ResourceManager:NSObject {
...
}
+(ResourceManager*)sharedResourceManager
@end

Затем вы всегда можете использовать [ResourceManager sharedResourceManager].

На самом деле, как говорит Роб в комментарии, вы можете полностью отказаться от +initialize в этом случае. Измените .m на что-то вроде

@implementation ResourceManager

+(ResourceManager*)sharedResourceManager
{
     static ResourceManager *g_ResManager=nil;
     if(!g_ResManager){
         g_ResManager=[[ResourceManager alloc] init];
     }
     return g_ResManager;
}
...

@end

Это идиома, которую я всегда использую лично. Но я предупреждаю вас, что это не полностью потокобезопасно! Он должен быть в порядке, если вы вызываете [ResourceManager sharedResourceManager] один раз перед тем, как создавать нересты, что я почти всегда буду делать, но это одна вещь, о которой нужно помнить. С другой стороны, версия, использующая +initialize, должна быть потокобезопасной, как есть, благодаря четко определенному поведению +initialize. См. Обсуждения в этом сообщении в блоге.

Ответ 2

Из документации для NSObject:

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

+initialize вызывается только перед отправкой первого его класса. Пока вы не отправите сообщение классу, метод +initialize не будет вызываться.

Например:

Если вы вызываете [[MyObject alloc] init];, в MyObject вызывается +initialize непосредственно перед отправкой alloc в MyObject.

Ответ 3

Помните, что если вы не распределяете объект самостоятельно, вы должны его сохранить. Пример:

+ (NSPredicate *)somePredicate
{
    static NSPredicate *predicate = nil;
    if (!predicate) {
        predicate = [NSPredicate predicateWithFormat:@"status == 2"];
        [predicate retain];
    }
    return predicate;
}

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