В чем разница между строковой константой и строковым литералом? - PullRequest
58 голосов
/ 25 августа 2008

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

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

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

Ответы [ 3 ]

83 голосов
/ 25 августа 2008

В 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 имеет значение; оно гарантирует, что сам указатель гарантированно будет постоянным.)

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

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

12 голосов
/ 08 сентября 2012

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

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

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

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

A константа будет 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";
3 голосов
/ 25 августа 2008

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

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

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

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

someMethod(mystring);

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

someMethod("my string");

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...