Objective-C директива #define, демонизированная для строковых констант
Я читаю несколько сообщений и в руководствах Apple, которые в Objective-C Строковые константы должны быть определены как extern NSString *const MY_CONSTANT;
и что следует избегать директивы #define. Почему это? Я знаю, что #define
запускается в прекомпиляцию, но вся строка будет иметь один и тот же адрес памяти. Единственное преимущество, которое я прочитал, состояло в том, что если константа должна быть обновлена или изменена, вам не придется перекомпилировать весь проект. Так что я должен объяснить, почему #define следует избегать?
Спасибо
UPDATE: В этом случае полезно использовать #define или есть лучший подход?
/* Constants Definition */
#define SERVER_URL @"http://subdomain.domain.edu.ar/Folder/"
NSString *const ServerURL = SERVER_URL;
NSString *const LoginURL = [email protected]"welcome.asp";
NSString *const CommandURL = [email protected]"com.asp";
Ответы
Ответ 1
Практическая причина использования константы в отличие от определения заключается в том, что вы можете делать прямые сравнения (используя ==) вместо использования isEqual:
. Рассмотрим:
NSString * const kSomeStringConstant = @"LongStringConstantIsLong";
...
[someArray addObject:kSomeStringConstant];
if ([someArray lastObject] == kSomeStringConstant)
{
...
}
Это будет работать, поскольку сравнение ==
будет сравнивать идентичные константные указатели с одним объектом NSString
. Используя #define
, однако:
#define STRING_CONSTANT @"MacrosCanBeEvil";
...
[SomeArray addObject:STRING_CONSTANT]; // a new const `NSString` is created
if ([someArray lastObject] == STRING_CONSTANT) // and another one, here.
{
...
}
Это не сработает, поскольку две строки будут иметь уникальные указатели. Чтобы эффективно сравнивать их, вам нужно будет выполнить сравнение по каждому символу с помощью isEqual:
if ([[someArray lastObject] isEqual:STRING_CONSTANT])
{
...
}
Это может быть гораздо более дорогостоящим с точки зрения времени выполнения, чем простое сравнение ==
.
Другой мотивацией может быть размер самого исполняемого файла. #defined constant будет фактически отображаться на месте, где бы она не использовалась в коде. Это может означать, что строка отображается много раз в вашем исполняемом файле. Напротив, константа должна (с современными компиляторами) быть определена только один раз, и все дальнейшие применения будут ссылаться на указатель на это одно определение.
Теперь, прежде чем кто-либо кричит на меня о преждевременной оптимизации, учтите, что два подхода почти идентичны с точки зрения реализации, но метод указателя const намного превосходит по размеру кода и времени выполнения.
Ответ 2
Не обязательно гарантировать, что только один объект NXConstantString для заданного строкового литерала во всем приложении. Похоже, что разные единицы компиляции могут иметь разные объекты для одной и той же константной строки. Например, если кто-то пишет плагин, будет создана одна константная строка для вхождения этого литерала NSString в плагин, и один будет сгенерирован для вхождений в хост-приложении, и они не будут равны указателю.
Ответ 3
Самый лучший аргумент, который я слышал, состоит в том, что строки const
отображаются в отладчике, тогда как макросы - нет.
Ответ 4
static NSString * const SERVER_URL = @"http://subdomain.domain.edu.ar/Folder/";
Ответ 5
Насколько я знаю, #define
позволяет вам определять строковые константы в стиле C. Чтобы создать постоянный объект NSString, вы должны объявить его в заголовке, а затем дать ему значение в одном из ваших .m файлов.
Заголовочный файл:
extern NSString *MyConstantString;
Основной файл:
NSString *MyConstantString = @"String value";