2009-12-08 4 views
11

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

extern NSString * const MyPluginErrorDomain; 

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

NSString * const MyPluginErrorDomain = @"MyPluginErrorDomain"; 

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

Затем я попытался

#define MYPLUGIN_ERROR_DOMAIN @"MyPluginErrorDomain" 

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

+0

удалить C, C++ теги - это объективный вопрос C – zaharpopov

+0

Отмеченный для C++, см. Также мой комментарий к интересному ответу Нила Баттерворта. Я все еще задаюсь вопросом, не существует ли также решения C, я не могу быть первым человеком в истории C, который хотел достичь этого. –

+0

Так как это константы C, этот C не нужно удалять. – iCaramba

ответ

10

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

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

3

В C++, я бы объявить их в заголовке, как это:

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

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

+0

Является локальным для единиц перевода *, действительным для всех определений указателей const? –

+1

Это верно для всех определений констант - указатели - это всего лишь один случай. Обратите внимание, что сам указатель должен быть const, поэтому const char * p = "foo"; не сделал бы. – 2009-12-08 11:57:16

+0

Для тех, кто, как и я, нашел это удивительным: «Имя с областью пространства имен имеет внутреннюю связь, если это имя .. объекта или ссылки, которая явно объявлена ​​как const, и не явно объявлено extern ..» [3.5/3 ] – 2009-12-08 12:43:16

9

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

extern NSString * const MyPluginErrorDomain; 

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

NSString * const MyPluginErrorDomain = @"MyPluginErrorDomain"; 

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

2

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

ехЬегп NSString * сопзЬ MyPluginErrorDomain;

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

/* h */ 

extern NSString * MyPluginErrorDomain(); 

/* m */ 

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

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

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

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

Say «MyPluginErrorDomain» и «ключевых» имеют идентичные строковые значения, но определяются в разных двоичных файлах (то есть на хосте плагина, один в плагине).

/////// 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, это одно, но использование во время выполнения - другое, есть несколько предостережений в деталях реализации и времени выполнения.

+0

строки 'extern' не будут работать, так как вам не нужно связываться с двоичным файлом, чтобы связываться с ним через удаленный обмен сообщениями' protocol' - think * * –

+0

@gf Это не редкость для плагина, который ссылается на конкретную библиотеку к интерфейсу плагина. – justin

+0

* «Не редкость» * не * «всегда» * - удаленный обмен сообщениями также * «общий» * с какао. –