Статическое использование NSString против встроенных констант NSString
В Objective-C я понимаю, что директива @ "foo" определяет константу NSString. Если я использую @ "foo" в нескольких местах, ссылается на тот же неизменный объект NSString.
Почему я так часто вижу этот фрагмент кода (например, в повторном использовании UITableViewCell):
static NSString *CellId = @"CellId";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellId];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:CellId];
Вместо просто:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellId"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:@"CellId"];
Я предполагаю, что это защитит меня от создания опечатки в имени идентификатора, который компилятор не поймал. Но если так, я не могу просто:
#define kCellId @"CellId"
и избежать статического бит NSString *? Или я что-то упускаю?
Ответы
Ответ 1
Хорошая практика превращать литералы в константы, потому что:
- Это помогает избежать опечаток, как вы сказали
- Если вы хотите изменить константу, вам нужно изменить ее только в одном месте.
Я предпочитаю использовать static const NSString*
static NSString* const
, потому что он немного безопаснее, чем #define
. Я стараюсь избегать препроцессора, если я действительно не нуждаюсь в нем.
Ответ 2
Мне нравятся все ответы здесь без простого примера того, как правильно объявить один... так...
Если вы хотите, чтобы константа была внешне видимой (т.е. "глобальная" ).... объявляйте ее как таковой в заголовке...
extern NSString *const MyTypoProneString;
и определите его в файле .m
, ВНЕШНИЙ любой @implementation
, как...
NSString * const MyTypoProneString = @"iDoNtKnOwHoW2tYpE";
Тем не менее, если вы просто хотите static const
, чтобы ЯВЛЯЕТСЯ ЛОКАЛЬНЫМ для реализации вашего класса (или даже определенного метода!)... просто объявите строку INSIDE реализация (или метод) как...
static NSString *MavisBeacon = @"She a freakin' idiot";
EDIT Хотя я и показываю, как это сделать... Я еще не убежден, что этот стиль ничуть лучше, чем смехотворно более короткое, более простое и менее повторяющееся объявление SINGLE, ла..
#define SomeStupidString @"DefiningConstantsTwiceIsForIdiots"
Используйте #define
... они намного менее раздражают. Просто не позволяйте препроцессорным игрокам ненавидеть вас.
Ответ 3
Вы должны сделать статическую переменную const
.
Одно отличие между статической переменной и макросом заключается в том, что макросы плохо работают с отладчиками. Макросы также не относятся к типу.
Большая часть рекомендаций static-var-vs-macro для C и С++ применима к Obj-C.
Ответ 4
Не гарантируется, что при использовании @"foo"
в нескольких местах среда выполнения использует для них одно и то же хранилище и, конечно же, может не иметь места в пределах блока компиляции или границ библиотеки.
Я предпочел бы использовать static NSString *string = @"foo"
, особенно с большим количеством литералов.
Ответ 5
Я предполагаю, что это защитит меня от создания опечатки в имени идентификатора, который компилятор не поймал.
Правильно. Это просто базовая защитная практика программирования. Скомпилированный результат (надеюсь) одинаковый в любом случае.
Но если так, я не могу просто:
#define kCellId @"CellId"
и избежать статического бит NSString *? Или я что-то упускаю?
Да. Но символ kCellId
будет определяться глобально, по крайней мере, в вашем блоке компиляции. Объявление статической переменной делает символ локальным для этого блока.
Обычно вы увидите строковые константы, определяемые как глобальные переменные или статические переменные, а не препроцессор. Это помогает убедиться, что вы используете только один экземпляр строки между разными единицами компиляции.
Ответ 6
Итак, я займусь этим немного позже, но это было задано много раз по-разному в обеих областях C/С++ SO, но в основном здесь расширенная версия моего комментария для alex gray:
В любое время, когда вы думаете, что вы должны использовать #define для строковых макросов, вы, скорее всего, не должны. Причина в том, что макросы #define в основном заменяют регулярные выражения на препроцессор. Каждый раз, когда препроцессор видит макрос, он заменяет его тем, что вы определили. Это означает, что новый строковый литерал каждый раз будет выделен в память, что очень плохо в таких местах, как идентификаторы повторного использования ячеек (поэтому код Apple UITableViewController по умолчанию использует статический).
Использование extern/static const вместо этого указывает все ссылки на одно место в памяти, как упоминал Эниль. Это намного эффективнее и эффективнее для памяти, что очень важно для мобильных устройств.