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

Согласно Программирование какао для 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 с этими определенными? Или выходить на мировой рынок - это действительно лучшая практика? Есть ли лучший образец, чем любой из них, учитывая, что многие люди считают синглтонов глобальными существами в красивом платье?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
19
0
55 702
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Ответ принят как подходящий

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

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

[myArray setObject:theObject forKey:MyGlobalVariableKeyName];

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • Каждый урок в вашей программе
  • Каждый #define
  • Каждое перечисление
  • Почти каждое имя, объявленное какао (за исключением редких глобальных переменных, таких как 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» на самом деле ничего не значит; В любом случае константность не будет учитываться при отправке сообщений. Строковые константы в Какао обычно объявляются <code> extern NSString * const NSFoo; </code>, что означает, что <em> указатель </em> доступен только для чтения. Строковые объекты, конечно, неизменяемы.

Jens Ayton 04.12.2008 03:19

Блин, если я не ищу его каждый раз, я всегда ошибаюсь в указателях на константы. Спасибо за улов. И да, в ответах тоже должны быть теги кода!

Barry Wark 02.01.2009 19:58

Что плохого в том, чтобы отказаться от const? это полезно для NSString? учитывая, что они неизменны независимо.

pablasso 19.02.2010 11:57

О, я получил свой ответ от вашего ответа здесь: goo.gl/tTFf Я каждый раз чувствовал грязный кастинг :)

pablasso 19.02.2010 12:04

Используйте обратные кавычки ` для кода в комментариях (и на самом деле в ответах) и звездочки * для выделения.

jbrennan 05.04.2010 21:05

опираясь на отличные ответы @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);

Другие вопросы по теме