Objective-C Безопасно ли перезаписывать [инициализация NSObject]? - PullRequest
5 голосов
/ 12 января 2011

По сути, у меня есть следующий код (объясненный здесь: Константы Objective-C в протоколе )

// MyProtocol.m
const NSString *MYPROTOCOL_SIZE;
const NSString *MYPROTOCOL_BOUNDS;

@implementation NSObject(initializeConstantVariables)

+(void) initialize {
     if (self == [NSObject class])
     {
         NSString **str = (NSString **)&MYPROTOCOL_SIZE;
         *str = [[MyClass someStringLoadedFromAFile] stringByAppendingString:@"size"];
         str = (NSString **)&MYPROTOCOL_BOUNDS;
         *str = [[MyClass someStringLoadedFromAFile] stringByAppendingString:@"bounds"];
     }
}

@end

Мне было интересно: безопасно ли мне иметь категорию, которая переопределяет метод +initialize NSObject?

Ответы [ 3 ]

14 голосов
/ 12 января 2011

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

+load имеет более предсказуемую ичетко определенное поведение, но происходит слишком рано, чтобы сделать что-нибудь полезное, потому что многие вещи находятся в неинициализированном состоянии.

Лично я вообще пропускаю +load или +initialize и использую аннотацию компилятора, чтобы вызвать функциювыполняться при загрузке базового бинарного файла / dylib.Тем не менее, в это время вы мало что можете сделать безопасно.

__attribute__((constructor))
static void MySuperEarlyInitialization() {...}

Вам гораздо лучше выполнять инициализацию в ответ на вызываемое приложение.NSApplication и UIApplication оба предлагают хуки делегата / уведомления для добавления небольшого количества кода в приложение при его запуске.

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

Почему бы вам не настроить эти две переменные внутри класса MyClass?А также, почему вы не используете аксессоры?Вместо того, чтобы иметь поддельную постоянную переменную, которая приходит из ниоткуда, предпочтительно определить метод доступа к классу, который должен фактически использовать его.Простой + (NSString *) myProtocolSize;было бы здорово, даже в протоколе.

Кроме того, переопределение методов класса в категории «работает», но не надежно и его следует избегать любой ценой: если метод, который вы переопределяете, реализованв категории среда выполнения НЕ гарантирует порядок загрузки, и ваша реализация может никогда не быть добавлена ​​к нему.

0 голосов
/ 12 января 2011

Переменная типа NSString *const всегда будет инициализироваться до запуска вашего кода, при условии, что мы игнорируем капризы C ++ на данный момент:

NSString *const MY_PROTOCOL_SIZE = @"...";

Переменная типа const NSString * (ключевое слово constприменение к кишкам NSString, а не к его адресу) может быть изменено любым кодом, но не может иметь отправленные ему сообщения.Это побеждает цель сделать это const.Вместо этого рассмотрим глобальную функцию:

static NSString *GetMyProtocolSize(void) {
    return [[MyClass someStringLoadedFromAFile] ...];
}

Или используйте метод класса:

@implementation MyClass
+ (NSString *)myProtocolSize {
    return [[MyClass someStringLoadedFromAFile] ...];
}
@end

Ранее вы задавали вопрос о том, почему строки const не принимают динамические значения--это потому, что вы, кажется, не понимаете, что const делает с символом.Вы должны прочитать значение ключевого слова const в C и рассмотреть другой подход к получению строк, если const не правильный.

...