В чем разница между строковой константой и строковым литералом?

Я изучаю цель C и Какао и наткнулся на это утверждение:

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

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

3 ответа

Решение

В Objective-C, синтаксис @"foo" является неизменным, буквальным примером NSString, Он не создает постоянную строку из строкового литерала, как предполагает Майк.

Компиляторы Objective-C обычно выполняют интернализацию литеральных строк в единицах компиляции, т. Е. Они объединяют многократное использование одной и той же литеральной строки, и для компоновщика возможно дополнительное интернирование между единицами компиляции, которые напрямую связаны в один двоичный файл. (Поскольку Какао различает изменяемые и неизменяемые строки, а литеральные строки также всегда неизменны, это может быть простым и безопасным.)

С другой стороны, константные строки обычно объявляются и определяются с использованием следующего синтаксиса:

// MyExample.h - declaration, other code references this
extern NSString * const MyExampleNotification;

// MyExample.m - definition, compiled for other code to reference
NSString * const MyExampleNotification = @"MyExampleNotification";

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

Хотя запись памяти не так сложна, как это было во времена рабочих станций 68030 с частотой 25 МГц и 8 МБ ОЗУ, сравнение строк на равенство может занять некоторое время. Обеспечение того, что большинство строк времени, которые равны, также будет равносильно указателю.

Скажем, например, вы хотите подписаться на уведомления от объекта по имени. Если вы используете непостоянные строки для имен, NSNotificationCenter публикация уведомления может привести к выполнению большого количества побайтных сравнений строк при определении того, кто в нем заинтересован. Если большинство этих сравнений имеют короткое замыкание, потому что сравниваемые строки имеют одинаковый указатель, это может быть большой победой.

Некоторые определения

Литерал - это значение, которое является неизменным по определению. например: 10
Константа - это переменная или указатель только для чтения. например: const int age = 10;
Строковый литерал - это выражение типа @"", Компилятор заменит это экземпляром NSString,
Строковая константа - это указатель только для чтения NSString, например: NSString *const name = @"John";

Некоторые комментарии к последней строке:

  • Это постоянный указатель, а не постоянный объект 1. objc_sendMsg 2 не волнует, если вы квалифицируете объект с const, Если вы хотите неизменный объект, вы должны закодировать эту неизменность внутри объекта 3.
  • Все @"" выражения действительно неизменны. Они заменяются 4 во время компиляции с экземплярами NSConstantString, который является специализированным подклассом NSString с фиксированной разметкой памяти 5. Это также объясняет, почему NSString это единственный объект, который может быть инициализирован во время компиляции 6.

Постоянная строка будет const NSString* name = @"John"; что эквивалентно NSString const* name= @"John";, Здесь и синтаксис, и намерение программиста ошибочны: const <object> игнорируется, а NSString пример (NSConstantString) была уже неизменной.

1 Ключевое слово const применяется относится к тому, что находится непосредственно слева от него. Если слева от него ничего нет, это относится ко всему, что находится справа от него.

2 Это функция, которую среда выполнения использует для отправки всех сообщений в Objective-C, и, следовательно, то, что вы можете использовать для изменения состояния объекта.

3 Пример: в const NSMutableArray *array = [NSMutableArray new]; [array removeAllObjects]; const не мешает последнему утверждению.

4 Код LLVM, который переписывает выражение: RewriteModernObjC::RewriteObjCStringLiteral в RewriteModernObjC.cpp.

5 Чтобы увидеть NSConstantString определение, cmd+ нажмите его в Xcode.

6 Создание констант времени компиляции для других классов будет простым, но для этого потребуется, чтобы компилятор использовал специализированный подкласс. Это нарушит совместимость со старыми версиями Objective-C.


Вернуться к вашей цитате

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

Это говорит о том, что литералы подвержены ошибкам. Но это не говорит о том, что они также медленнее. Для сравнения:

// string literal
[dic objectForKey:@"a"];

// string constant
NSString *const a = @"a";
[dic objectForKey:a];

Во втором случае я использую ключи с указателями const, поэтому вместо [a isEqualToString:b], Я могу сделать (a==b), Реализация isEqualToString: сравнивает хеш, а затем запускает функцию C strcmp так что это медленнее, чем прямое сравнение указателей. Вот почему константы лучше: они быстрее сравниваются и менее подвержены ошибкам.

Если вы также хотите, чтобы ваша постоянная строка была глобальной, сделайте это так:

// header
extern NSString *const name;
// implementation
NSString *const name = @"john";

Давайте использовать C++, поскольку мой Objective C полностью отсутствует.

Если вы прячете строку в постоянную переменную:

const std::string mystring = "my string";

Теперь, когда вы вызываете методы, вы используете my_string, вы используете строковую константу:

someMethod(mystring);

Или вы можете вызывать эти методы напрямую со строковым литералом:

someMethod("my string");

Предположительно, причина в том, что они поощряют вас использовать строковые константы, потому что Objective C не выполняет "интернирование"; то есть, когда вы используете один и тот же строковый литерал в нескольких местах, это фактически другой указатель, указывающий на отдельную копию строки.

Для ключей словаря это имеет огромное значение, потому что, если я увижу, что два указателя указывают на одно и то же, это намного дешевле, чем сравнение целых строк, чтобы убедиться, что строки имеют одинаковое значение.

Редактировать: Майк, в C# строки являются неизменяемыми, и литеральные строки с одинаковыми значениями заканчиваются на одном и том же строковом значении. Я полагаю, что это верно и для других языков, которые имеют неизменные строки. В Ruby, который имеет изменяемые строки, они предлагают новый тип данных: символы ("foo" против:foo, где первая является изменяемой строкой, а вторая является неизменяемым идентификатором, часто используемым для ключей Hash).

Другие вопросы по тегам