Подтвердить что ты не робот

Глобальные переменные в Cocoa/Objective-C?

Согласно Cocoa Программирование для Mac OS X, 3rd Edition, на стр. 202 (глава 13):

Вы будете регистрироваться, читать и установка значений по умолчанию в нескольких классах в ваше приложение. Чтобы убедиться, что вы всегда используете одно и то же имя, вы должны объявить эти строки в один файл, а затем просто #import этот файл в любой файл, в котором вы используйте имена. Существует несколько способов сделать это. Например, вы можете использовать команда препроцессоров С#define, но большинство Cocoa программистов используют глобальные переменных для этой цели.

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

Будет ли лучший дизайн простым классом Singleton с указанными? Или действительно ли правильная передовая практика - глобальная? Есть ли лучший образец, чем любой, учитывая, что многие люди считают, что Singletons являются глобальными в хорошем платье?

4b9b3361

Ответ 1

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

Вместо этого:

[myArray setObject:theObject forKey:MyGlobalVariableKeyName];

Вам нужно будет что-то сделать по строкам:

[myArray setObject:theObject 
            forKey:[[MySingletonVariableClass getInstance] myVariableKeyName];

Глобальные переменные существенно меньше типируют для одного и того же эффекта.

Ответ 2

Чтобы быть ясным, рекомендуется создавать неизменные глобальные переменные вместо строковых строковых констант (трудно рефакторировать и не проверять время компиляции) или #defines (без проверки времени компиляции). Вот как вы можете это сделать...

в MyConstants.h:

extern NSString * const MyStringConstant;

в MyConstants.m:

NSString * const MyStringConstant = @"MyString";

затем в любом другом файле .m:

#import "MyConstants.h"

...
[someObject someMethodTakingAString:MyStringConstant];
...

Таким образом, вы получаете проверку времени компиляции, что вы неправильно указали строковую константу, вы можете проверить равенство указателя, а не равенство строк [1] ​​при сравнении ваших констант, а отладка проще, поскольку константы имеют строковое значение времени выполнения.

[1] В этом использовании вы по существу используете значения указателя в качестве констант. Так получилось, что эти конкретные целые числа также указывают на строки, которые можно использовать в отладчике

Ответ 3

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

Это глобальная константа - глобальная по объему, но постоянная и, следовательно, не плохая в том смысле, что глобальные переменные являются плохими.

Чтобы показать, как глобальные константы являются общими, безопасными и многочисленными, рассмотрим эти примеры глобальных констант:

  • Каждый класс в вашей программе
  • Каждый #define
  • Каждое перечисление
  • Почти каждое имя, объявленное Cocoa (исключая редкие глобальные переменные, такие как NSApp).

Единственный раз, когда вы должны беспокоиться о глобальных константах, - это когда их имена слишком общие (они могут загрязнять глобальное пространство имен). Поэтому не используйте имена, которые могут конфликтовать с чем угодно (всегда используйте префикс и всегда указывайте имя как заданное как NSKeyValueObservingOptionNew).

Ответ 4

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

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

Ответ 5

построив на @Barry Wark и @Matt Gallagher отличные ответы, и мой первоначальный ответ (см. конец этого ответа) есть третий подход, и это должно использовать комбинацию макро /include, которая гарантирует, что вы только наберете имя переменной один раз, и поэтому он включен одновременно в файлы .h и .m.

<EDIT>

"всегда есть другой способ..."

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

в .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 или вы получите предупреждение, которое изменило определение.

< END EDIT >

ОРИГИНАЛЬНЫЙ ОТВЕТ, ДЕЙСТВИТЕЛЬНО ДЕЙСТВИТЕЛЬНО, НО БЕЗ РЕФИНЕМЕНТОВ

Сначала создайте файл 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.

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

Ответ 6

Так ведь. Я придумал 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);

Ответ 7

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

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

Или у вас может быть член Storagefile глобальной переменной User Preferences. Все еще может быть одноэлементным, но в данном случае это не имеет особого значения.

Для меня сложные значения по умолчанию (десятки разных типов классов) должны находиться в собственном "пространстве", доступном для модели.

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

Опять же, это зависит от вашего дизайна.