Каков наилучший способ определения строковых констант в протоколе objective-c?

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

extern NSString * const MyPluginErrorDomain;

в файле .h и в файле .m:

NSString * const MyPluginErrorDomain = @"MyPluginErrorDomain";

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

Затем я попробовал

#define MYPLUGIN_ERROR_DOMAIN @"MyPluginErrorDomain"

но классы реализации в подключаемом модуле, похоже, не видят #define. Кто знает хорошее решение?

Ответ 1

Вы можете объявить их в заголовке с протоколом (но вне самого интерфейса протокола), а затем определить их в файле реализации для протокола (очевидно, что он не будет иметь раздел @implementation - только ваш NSString определения).

Или иметь отдельную пару .h/.m, которая предназначена только для строковых констант (заголовок протокола может импортировать заголовок строковых констант).

Ответ 2

Вы сохраняете определение .h:

extern NSString * const MyPluginErrorDomain;

но поместите эту часть в отдельный файл .m, который входит в вашу структуру:

NSString * const MyPluginErrorDomain = @"MyPluginErrorDomain";

Так что плагины все еще могут реализовать интерфейс, но при компиляции они свяжутся или компилируются в другом файле .m, поэтому они будут видеть значение MyPluginErrorDomain.

Ответ 3

В С++ я объявляю их в заголовке следующим образом:

const char * const MYPLUGIN_ERROR_DOMAIN = "MyPluginErrorDomain";
const char * const MYPLUGIN_FOO_DOMAIN = "MyPluginFooDomain";

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

Ответ 4

Вы должны реализовать его как внешние строки, как в вашем примере:

extern NSString * const MyPluginErrorDomain;

или предоставить функции extern, которые возвращают данные статического хранилища. Например:

 /* h */ 

 extern NSString * MyPluginErrorDomain();

 /* m */ 

 NSString * MyPluginErrorDomain() {
    static NSString * const s = @"MyPluginErrorDomain";
    return s;
 }

Причина в том, что строки и ключи часто используются и сравниваются по значению указателя или хеш-значению, а не по сравнению с истинным сопоставлением строк (isEqualToString:).

На уровне реализации существует большая разница между:

В коде это означает, что когда сравниваемые строки определены в нескольких двоичных файлах:

Скажите, что "MyPluginErrorDomain" и "key" имеют одинаковые строковые значения, но определены в разных двоичных файлах (например, на хосте плагина, в плагине).

/////// Pointer comparison (NSString)
BOOL a = [MyPluginErrorDomain isEqualToString:key];
BOOL b = MyPluginErrorDomain == key;

// c may be false because a may be true, in that they represent the same character sequence, but do not point to the same object
BOOL c = a == b;


/////// Hash use (NSString)
// This is true
BOOL d = [MyPluginErrorDomain hash] == [key hash];

// This is indicative if true
BOOL e = [MyPluginErrorDomain hash] == [someOtherStringKey hash];

// because
BOOL f = [MyPluginErrorDomain isEqualToString:someOtherStringKey];

// g may be false (though the hash code is 'generally' correct)
BOOL g = e == f;

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

Хэш-коды и сравнения указателей используются во всех Foundation и других технологиях objc во внутренних словах хранения словарей, кодировании ключевых значений... Если ваш словарь идет прямо в xml, это одна вещь, но использование во время выполнения - другое, и там это несколько предостережений в деталях реализации и выполнения.