Согласно Программирование какао для Mac OS X, 3-е издание, на странице 202 (глава 13):
You will be registering, reading, and setting defaults in several classes in your application. To make sure that you always use the same name, you should declare those strings in a single file and then simply #import that file into any file in which you use the names. There are several ways to do this. For example, you could use the C preprocessor’s #define command, but most Cocoa programmers use global variables for this purpose.
Это действительно лучший метод? Глобальные переменные? Мне это кажется безумием - вопреки всему, чему меня когда-либо учили.
Будет ли лучший дизайн простым классом Singleton с этими определенными? Или выходить на мировой рынок - это действительно лучшая практика? Есть ли лучший образец, чем любой из них, учитывая, что многие люди считают синглтонов глобальными существами в красивом платье?





Глобальные переменные или синглтон будут делать здесь то же самое. Оба могут использоваться для превращения «ключевых» имен в Какао, которые не будут вызывать ошибку компилятора, если она написана с ошибкой в ошибку компилятора. Это основная цель. Глобальные переменные немного проще, поскольку они требуют меньше ввода.
Вместо этого:
[myArray setObject:theObject forKey:MyGlobalVariableKeyName];
Вам нужно будет сделать что-то вроде:
[myArray setObject:theObject
forKey:[[MySingletonVariableClass getInstance] myVariableKeyName];
Глобальные переменные существенно меньше типизируют для того же эффекта.
Для меня приемлемы постоянные глобальные переменные, которые устанавливаются во время компиляции и никогда не меняются. Если вы жестко кодируете строку, это то же самое, только скрытое компилятором. Я буду избегать изменчивых глобалов, таких как чума.
Помните, что сама Apple использует ту же технику. Многие из констант, которые я ожидал определить, на самом деле являются константами. Вы получите ошибки ссылки, если заголовки доступны, а фреймворк - нет.
Это зависит от дизайна вашего программного обеспечения. Предположим, у вас есть программа для управления заданиями, и одна из ваших "настроек по умолчанию" - это список каталогов, в которых можно сохранять различные элементы.
Для каждого задания у вас может быть элемент storagefile, который является одноэлементным, который загружает предпочтительные пользователем местоположения при запуске.
Или у вас может быть член Storagefile глобальной переменной с именем User Preferences. Все еще может быть синглтон, но в данном случае это не имеет значения.
Для меня сложные значения по умолчанию (десятки различных типов классов) должны находиться в своем собственном «пространстве», доступном для модели.
Однако могут быть предпочтения, которые важны для настройки задания, поэтому эти предпочтения необходимо сохранить в объекте задания, чтобы при открытии его в приложении другого пользователя оно работало должным образом.
Опять же, это зависит от вашего дизайна.
Называть ее глобальной переменной технически правильно, но вводить в заблуждение.
Это глобальная константа - глобальная по объему, но постоянная и, следовательно, неплохая в том смысле, что глобальные переменные плохи.
Чтобы показать, насколько распространены, безопасны и многочисленны глобальные константы, рассмотрим эти примеры глобальных констант:
NSApp).Единственный случай, когда вам следует беспокоиться о глобальных константах, - это когда их имена слишком общие (они могут загрязнять глобальное пространство имен). Поэтому не используйте имена, которые могут с чем-либо конфликтовать (всегда используйте префикс и всегда делайте имя специфичным для задачи, например NSKeyValueObservingOptionNew).
Для ясности: рекомендуется создавать глобальные переменные неизменный вместо встроенных строковых констант (трудно поддающийся рефакторингу и без проверки во время компиляции) или #defines (без проверки во время компиляции). Вот как вы можете это сделать ...
в MyConstants.h:
extern NSString * const MyStringConstant;
в MyConstants.m:
NSString * const MyStringConstant = @"MyString";
затем в любом другом файле .m:
#import "MyConstants.h"
...
[someObject someMethodTakingAString:MyStringConstant];
...
Таким образом, вы получаете во время компиляции проверку того, что вы не неправильно написали строковую константу, вы можете проверять равенство указателей, а не равенство строк [1] при сравнении ваших констант, и отладка упрощается, поскольку константы выполняются -время строковое значение.
[1] В этом случае вы, по сути, используете значения указателя в качестве констант. Так уж получилось, что эти конкретные целые числа также указывают на строки, которые можно использовать в отладчике.
Блин, если я не ищу его каждый раз, я всегда ошибаюсь в указателях на константы. Спасибо за улов. И да, в ответах тоже должны быть теги кода!
Что плохого в том, чтобы отказаться от const? это полезно для NSString? учитывая, что они неизменны независимо.
О, я получил свой ответ от вашего ответа здесь: goo.gl/tTFf Я каждый раз чувствовал грязный кастинг :)
Используйте обратные кавычки ` для кода в комментариях (и на самом деле в ответах) и звездочки * для выделения.
опираясь на отличные ответы @Barry Wark и @Matt Gallagher, и мой первоначальный ответ (см. конец этого ответа) существует третий подход, а именно использование комбинации макроса / включения, которая гарантирует, что вы вводите имя переменной только один раз, и поэтому он включается одновременно в файлы .h и .m.
<РЕДАКТИРОВАТЬ>
"Всегда есть другой выход ..."
Подумав о том, как сделать это еще проще, без использования дополнительного файла заголовка, вот более краткий подход с использованием вложенных макросов.
в файле .h
#define defineKeysIn_h_File(key) extern NSString * const key;
#define defineKeysIn_m_File(key) NSString * const key = @#key;
#define myKeyDefineKeys(defineKey) \
/**start of key list*/\
defineKey(myKeyABC);\
defineKey(myKeyXYZ);\
defineKey(myKey123);\
/*end of key list*/
myKeyDefineKeys(defineKeysIn_h_File);
в файле .m
myKeyDefineKeys(defineKeysIn_m_File);
примечание о реализации
Вы можете использовать это более одного раза в нескольких заголовках, однако вам нужно изменить Чтобы имя «myKeyDefineKeys» было уникальным, я предлагаю дать ему тот же префикс, что и ключи, которые вы определяете - в качестве примера я использовал «myKey» повсюду.
В другом файле я мог бы использовать myOtherKeyDefineKeys.
Также не связывайтесь с макросами defineKeysIn_h_File и defineKeysIn_m_File, иначе вы получите предупреждение об изменении определения.
<КОНЕЦ РЕДАКТИРОВАНИЯ>
ОРИГИНАЛЬНЫЙ ОТВЕТ, ДЕЙСТВУЮЩИЙ, НО БЕЗ ДОПОЛНЕНИЙ
Сначала создайте файл vanilla.h и удалите #ifdef по умолчанию и т. д. И введите свои ключи, как показано ниже: (Это вырезано и вставлено из категории, которую я написал для расширения AVAudioPlayer)
// playFromConsts.h
define_key(AVAudioPlayer_key_player);
define_key(AVAudioPlayer_key_duration);
define_key(AVAudioPlayer_key_filename);
define_key(AVAudioPlayer_key_filepath);
define_key(AVAudioPlayer_key_fileurl);
define_key(AVAudioPlayer_key_urlString);
define_key(AVAudioPlayer_key_envelope);
define_key(AVAudioPlayer_key_startDate);
define_key(AVAudioPlayer_key_linkToPlayer);
define_key(AVAudioPlayer_key_linkFromPlayer);
define_key(AVAudioPlayer_key_linkToPlayerEnvelope);
define_key(AVAudioPlayer_key_linkFromPlayerEnvelope);
define_key(AVAudioPlayer_key_deviceStartTime);
define_key(AVAudioPlayer_key_currentVolume);
define_key(AVAudioPlayer_key_fadeFromVolume);
define_key(AVAudioPlayer_key_fadeToVolume);
define_key(AVAudioPlayer_key_fadeTime);
define_key(AVAudioPlayer_key_segueTime);
Затем в вашем файле normal.h (где объявлен ваш @interface, @protocol и т. д.) Поместите эти 3 строки (конечно, заменив файл заголовка)
#define define_key(x) extern NSString * const x;
#include "playFromConsts.h"
#undef define_key
наконец, в вашем .m файле, который связан с вашим файлом "@interface .h", поместите эти 3 строки:
#define define_key(x) NSString * const x = @#x;
#include "playFromConsts.h"
#undef define_key
обратите внимание на «#include», а не «#import» - мы действительно хотим включать этот файл более одного раза.
это сделает всю грязную работу и обеспечит, чтобы ключи были NSString * const.
трейлинг; не является обязательным, так как он включен в макрос, но лично я предпочитаю его.
Так ведь. Придумал 3 файла.
Constants.h
#define def_key(name) extern NSString *const name
#define def_int(name, value) extern int const name
#define def_type(type, name, value) extern type const name
#include "ConstantsDefs.h"
Constants.m
#import "Constants.h"
#undef def_key
#define def_key(name) NSString *const name = @#name
#undef def_int
#define def_int(name, value) int const name = value
#undef def_type
#define def_type(type, name, value) type const name = value
#include "ConstantsDefs.h"
ConstantsDefs.h
def_key(kStringConstant);
def_int(kIntConstant, 313373);
def_type(float, kFloatConstant, 313373.0f);
«Const NSString» на самом деле ничего не значит; В любом случае константность не будет учитываться при отправке сообщений. Строковые константы в Какао обычно объявляются <code> extern NSString * const NSFoo; </code>, что означает, что <em> указатель </em> доступен только для чтения. Строковые объекты, конечно, неизменяемы.