Static const Vs extern const
Я использую static const в своих файлах заголовков так:
static NSString * const myString = @"foo";
Но прочитали, что это не "безопасный" или правильный способ сделать это. По-видимому, если я хочу, чтобы мои константные строки были доступны из другого класса, я должен был объявить строку в моем .h как:
extern NSString * const myString;
Затем в файле .m:
NSString * const myString = @"foo";
Это правильно? Если да, то в чем причина не объявлять его как static непосредственно в моем .h файле? Он отлично работает, и я не вижу никаких проблем с безопасностью. Это const, поэтому он не может быть изменен извне и что-то, что я намеренно нуждаюсь в доступе за пределами класса. Единственное, что я могу придумать, это скрыть значение строки?
Ответы
Ответ 1
Ваш первый вариант
static NSString * const myString = @"foo"; // In .h file, included by multiple .m files
определяет переменную myString
локально в каждой "единицы перевода" (грубо говоря: в каждом исходном файле .m)
который включает файл заголовка. Все строковые объекты имеют одинаковое содержимое "foo",
но это могут быть разные объекты, так что значение myString
(указатель на строковый объект)
могут различаться в каждом блоке.
Ваш второй вариант
extern NSString * const myString; // In .h file, included by multiple .m files
NSString * const myString = @"foo"; // In one .m file only
определяет единственную переменную myString
, которая видна "глобально".
Пример: В одном классе вы отправляете уведомление с myString
в качестве объекта пользователя.
В другом классе это уведомление принимается, а объект пользователя по сравнению с myString
.
В вашем первом варианте сравнение должно выполняться с помощью isEqualToString:
, потому что
у отправляющего и принимающего класса могут быть разные указатели (оба указывающие на
NSString
объект с содержимым "foo" ). Поэтому сравнение с ==
может завершиться неудачей.
В вашем втором варианте есть только одна переменная myString
, поэтому вы можете сравнить ее с ==
.
Таким образом, второй вариант безопаснее в том смысле, что "общая строка" является одним и тем же объектом в каждой единицы перевода.
Ответ 2
Нет никакой причины, о которой я знаю, для объявления чего-либо внешнего в Objective-C, тогда как вы используете Objective-C только в своем проекте.
Я мог бы думать о причинах, когда вы смешиваете его с модулями C или ассемблера и т.д.
Однако extern
имеет то преимущество, что константа действительно будет существовать только во всем проекте, если это то, чего вы хотите достичь, если вам действительно нужно сохранить эти 20 или около того байтов.
Но это сопряжено с риском противоречивых имен. Другие библиотеки, возможно, объявили свои собственные внешние имена, используя одно и то же имя. И компоновщик позаботился бы о том, чтобы они использовали одно и то же пространство в памяти, хотя они могут быть разных типов.
И да, объявление extern
в заголовке должно сопровождаться соответствующим определением в файле .m. Я не уверен, но я думаю, что вы могли бы назначить @ "foo" в файле .h.
Вы даже можете объявить это за пределами @interface/@implementation- @end блоков. (Никогда не пробовал это сам). В этом случае переменная будет глобальной и доступна везде, даже без ключевого слова extern
. Во время компиляции компилятор будет жаловаться на доступ к ним, если он не увидит свое объявление в цепочке операторов #include. Но, академически, один файл .m может содержать два или более класса (что я, очевидно, не рекомендую), и тогда переменная будет доступна из обоих классов, хотя она не принадлежит ни одному из них.
В конце концов, OjbC является просто дополнением к ANSI C.
Однако нет смысла ставить их. Эти константы в любом случае статичны. Это константы. Цель статической переменной в классе или даже методе заключается в том, что область видимости ограничена этим классом, но есть только один экземпляр во время выполнения, который используется всеми экземплярами класса.
Пример:
@implementation AClass : NSObject
static NSString *someString
- (void) setString:(NSString*) aString{
someString = aString;
}
- (NSString*) getString (){
return someString;
}
... и где-то еще:
AClass * a = [[AClass alloc] init];
AClass * b = [[AClass alloc] init];
[a setString:@"Me"];
[b setString;@"You"];
NSLog (@"String of a: ", [a getString]);
выведет You
, но не Me
Если это то, что вы хотите, и только тогда используйте static.
Использование простых макросов препроцессора (которые я предпочитаю, но я вроде как oldschool здесь) имеет тот недостаток, что эти строки будут копироваться в двоичный каждый раз при использовании макроса. По-видимому, это не вариант для вас, потому что вы даже не просили их.
Однако для большинства применений макросы препроцессора в общедоступных файлах .h будут делать трюк управления константами в классах.
Ответ 3
Использование static NSString* const myString = @"foo";
в файле заголовка означает, что каждая единица перевода получает отдельную переменную myString
. Я думаю, что компоновщик может консолидировать их, но я не буду рассчитывать на это. Это означает, что код, который сравнивает строку, полученную с помощью if (someString == myString) ...
, может получить значение false, даже если вызывающий передал myString
в, если вызывающий абонент был из другой единицы перевода. (Конечно, код должен использовать -isEqualToString:
вместо ==
, но с правильно объявленной строковой константой последний может быть работоспособен.)
Ответ 4
Преимущество внешнего способа описано в других ответах.
Одним из возможных преимуществ статического способа, если переменная не была объектом (NSString
в вашем случае), но примитивного типа (например, целое число), заключается в том, что компилятор способен оптимизировать доступ к константе:
Когда вы объявляете const в своей программе,
int const x = 2;
Компилятор может оптимизировать этот const, не обеспечивая хранение этой переменной, а добавляя ее в таблицу символов. Таким образом, последующее чтение просто нуждается в косвенности в таблице символов, а не в инструкциях для извлечения значения из памяти.
Взято из этого ответа.
Ответ 5
Когда дело доходит до классов хранения, static означает одну из двух вещей.
Статическая переменная внутри метода или функции сохраняет свое значение между вызовами.
Можно назвать статическую переменную, объявленную глобально любой функцией или методом, если эти функции отображаются в тот же файл, что и статическая переменная. То же самое касается статических функций.
В то время как static делает функции и переменные глобально видимыми в определенном файле, extern делает их видимыми глобально для всех файлов.
Каждый раз, когда ваше приложение использует строковую константу с неязыковым значением в открытом интерфейсе, оно должно объявлять ее как внешнюю строчную константу.
Шаблон должен объявить extern NSString * const в общедоступном заголовке и определить, что NSString * const в реализации.
Источник: nshipster.com/c-storage-classes
Ответ 6
Но прочитали, что это не "безопасно" или правильно настораживает это.
Это безопасно, если программа не многопоточная. В этом случае он небезопасен, если вы не защитите глобальную переменную с помощью мьютекса.
Однако это неверно. NSString * const myString
означает постоянный указатель на (непостоянные) данные. Скорее всего, вы хотите, чтобы переменная сама была постоянной: const NSString* myString
.
По-видимому, если я хочу, чтобы мои константные строки были доступны из другого класса, я должен объявить строку в моем .h как: extern
...
Правильно. Обратите внимание, что extern
допустимо только для констант. Это неприемлемо для непостоянных переменных: глобальные переменные считаются очень плохой практикой.
если да, то в чем причина не просто использовать объявить его как static непосредственно в моем файле .h, так как он отлично работает
Единственная причина, по которой вы хотели бы объявить ее как статическую, - это то, что вы хотите ограничить область действия переменной локальным .c файлом, другими словами, частным инкапсуляцией. Поэтому не имеет смысла объявлять статические переменные в файле .h.
Это const, поэтому его нельзя изменить извне
Его можно изменить, потому что он не const, см. мое первое замечание.
Как правило, не делайте таких вещей. Все вышеперечисленное означает, что у вас есть недостатки в вашем самом программном проекте, которые необходимо исправить. Вы должны проектировать свою программу объектно-ориентированным образом, чтобы каждый модуль кода был автономным и не знал или не заботился ни о чем, кроме назначенной задачи.
Ответ 7
#define myString @"foo"
Вот пример:
Вы сможете объединить строки во время компиляции:
NSLog(@"%@", @"Say hello to " myString);
выведет: произнесите привет в foo