Где хранить глобальные константы в приложении iOS?

Большинство моделей в моем приложении для iOS запрашивают веб-сервер. Я хотел бы иметь файл конфигурации, в котором хранится базовый URL-адрес сервера. Это будет выглядеть примерно так:

// production
// static NSString* const baseUrl = "http://website.com/"

// testing
static NSString* const baseUrl = "http://192.168.0.123/"

Комментируя ту или иную строку, я могу мгновенно изменить сервер, на который указывают мои модели. Мой вопрос: как лучше всего хранить глобальные константы в iOS? В программировании Android у нас есть встроенный файл ресурсов строк. В любом Activity (эквивалент UIViewController), мы можем получить эти строковые константы с помощью:

String string = this.getString(R.string.someConstant);

Мне было интересно, есть ли в iOS SDK аналогичное место для хранения констант. Если нет, что лучше всего сделать в Objective-C?


person JoJo    schedule 19.08.2011    source источник


Ответы (11)


Вы также можете сделать

#define kBaseURL @"http://192.168.0.123/"

в заголовочном файле "констант", скажем, constants.h. Тогда сделай

#include "constants.h"

вверху каждого файла, в котором вам нужна эта константа.

Таким образом, вы можете переключаться между серверами в зависимости от флагов компилятора, например:

#ifdef DEBUG
    #define kBaseURL @"http://192.168.0.123/"
#else
    #define kBaseURL @"http://myproductionserver.com/"
#endif
person Cyrille    schedule 19.08.2011
comment
Я использую подход "constants.h", объявляя static переменные на основе #ifdef VIEW_CONSTANTS ... #endif. Итак, у меня есть один файл констант для всего приложения, но каждый из моих других файлов кода #defines разные наборы констант, которые нужно включить перед #include-включением файла констант (останавливает все определенные, но не используемые предупреждения компилятора). - person ; 19.08.2011
comment
При использовании этого решения я столкнулся с двумя проблемами. Во-первых, когда я использовал #decalare, у меня возникла ошибка компиляции, в которой говорилось: invalid preprocessing directive declare. Поэтому я заменил его на #define. Другая проблема заключается в использовании константы. Я хотел создать другую константу с static NSString* const fullUrl = [NSString stringWithFormat:@"%@%@", kbaseUrl, @"script.php"], но, очевидно, создавать константы с выражением незаконно. Я получаю сообщение об ошибке элемент инициализатора не является постоянным. - person JoJo; 19.08.2011
comment
В самом деле, это #define, а не #declare. Моя ошибка, исправляю свой ответ. Однако странно, что вы получаете эту ошибку. Это связано с частью static NSString* const fullURL, а не с использованием константы (это макрос препроцессора, так что все как если бы вы написали ...@"%@%@", @"something", @"script.php"). Кстати, вместо использования stringWithFormat вы могли бы просто написать [kBaseUrl stringByAppendingString:@"script.php"], что занимает меньше времени (нет необходимости анализировать строку формата). - person Cyrille; 20.08.2011
comment
Один супер-ботаник из моей компании сказал мне, что нельзя создавать постоянные объекты из выражения других объектов. В этом случае мы не можем создать константу NSString fullUrl из другой константы NSString baseUrl. Это потому, что объекты по своей природе создаются во время выполнения. Вместо этого этот супер-ботаник сказал мне просто хранить мои постоянные строки как char*, чтобы избежать этой ошибки компиляции. char* можно легко преобразовать в NSString, когда это необходимо множеством вызовов API iOS, которым требуются NSStrings. Это мое окончательное решение. - person JoJo; 20.08.2011
comment
@JoJo, как можно объединить строки с помощью char *? Не могли бы вы показать код? - person Fred Collins; 07.04.2012
comment
Я знаю, что это старый ответ, но будет ли работать включение файла constants.h в файл приложений .pch? Чтобы константы действительно были глобальными? - person Piotr Tomasik; 12.10.2012
comment
Мне почему-то не нужно импортировать файл заголовка, я могу нормально использовать константы, не ссылаясь на них. - person Marius Soutier; 12.11.2012
comment
@Cyrille Android действительно интересно практиковать, есть некоторые возможности, которые вы не могли себе представить на iOS! В любом случае спасибо за ответ - person klefevre; 26.02.2013
comment
@ kl94 Я отказываюсь троллить Java в Stack Overflow;) - person Cyrille; 26.02.2013
comment
@Cyrille: Думаю, я тоже могу много троллить с Java ^^ Я говорил об Android и его возможностях. Не Ява и ее уродство! (Упс тролль: D). хорошего дня ! - person klefevre; 28.02.2013
comment
По возможности предпочитайте константу #define - вы получите лучшую проверку во время компиляции, и отладка будет работать лучше. - person occulus; 23.03.2013
comment
как получить доступ к определенной переменной? Когда я использую переменную, я всегда получаю сообщение об ожидаемом выражении. например NSString * str = kBaseUrl или NSString * str = @ kBaseUrl. Ни то, ни другое не работает в моем случае ... Должно быть, я что-то упустил ... - person Anson Yao; 30.10.2014
comment
@AnsonYao обычно, когда это случается со мной, я забываю удалить точку с запятой из #define, например #define kBaseURL @"http://192.168.0.123/"; - person Gyfis; 12.04.2015

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

Кроме того, предпочтительнее просто объявить символ extern NSString* const, а не использовать #define:


SomeFile.h

extern NSString* const MONAppsBaseUrl;

SomeFile.m

#import "SomeFile.h"

#ifdef DEBUG
NSString* const MONAppsBaseUrl = @"http://192.168.0.123/";
#else
NSString* const MONAppsBaseUrl = @"http://website.com/";
#endif

Помимо отсутствия декларации Extern, совместимой с C ++, это то, что вы обычно увидите в фреймворках Apple Obj-C.

Если константа должна быть видна только одному файлу или функции, тогда static NSString* const baseUrl в вашем *.m подойдет.

person justin    schedule 13.08.2012
comment
Не уверен, почему в принятом ответе было 40 голосов за #define - const действительно лучше. - person occulus; 23.03.2013
comment
Определенно const NSString лучше, чем #define, это должен быть принятый ответ. #define создает новую строку каждый раз, когда используется определенное значение. - person jbat100; 19.07.2013
comment
@ jbat100 Я не думаю, что он создает новую строку. Я думаю, что компилятор определяет, создает ли ваш код одну и ту же статическую строку 300000 раз, и создаст ее только один раз. @"foo" - это не то же самое, что [[NSString alloc] initWithCString:"foo"]. - person Abhi Beckert; 19.07.2013
comment
@AbhiBeckert, я думаю, что jbat пытался сделать то, что можно получить дубликаты вашей константы, когда используется #define (т.е. равенство указателя может не сработать) - а не то, что буквальное выражение NSString создает временное при каждом выполнении. - person justin; 19.07.2013
comment
Я согласен с тем, что #define - плохая идея, я просто хотел исправить сделанную им ошибку, заключающуюся в создании нескольких объектов. Кроме того, на равенство указателей нельзя полагаться даже для констант. Он может быть загружен из NSUserDefaults или чего-то еще. Всегда используйте isEqual :. - person Abhi Beckert; 19.07.2013
comment
@AbhiBeckert технически может привести к нескольким случаям. вы, вероятно, не часто видите это в программах iOS (где такие определения будут в изображении, которое связано статически), потому что повторяющиеся строковые константы в изображении могут быть удалены (не только строки C, но и строки NS / CF). поэтому ваши литералы помещаются в раздел изображения __TEXT .__ cstring.cstring_literals, а CFStrings - в __DATA .__ cfstring. поэтому (как вы говорите) вполне разумно ожидать, что повторяющиеся экземпляры, созданные с помощью литеральных выражений nscfstring, будут одним и тем же экземпляром в вашем приложении (но не гарантия). - person justin; 19.07.2013
comment
@AbhiBeckert, но если, скажем, имена уведомлений были объявлены с использованием #define в заголовке библиотеки, константы (полученные путем текстовой подстановки определения) использовались каждым изображением, и библиотека загружалась динамически, затем каждое изображение (например, ваше приложение и динамическая библиотека / фреймворк) будет иметь отдельные экземпляры этой #defined «константы», потому что каждое изображение будет иметь отдельные разделы данных, на которые они ссылаются. Опять же, как вы говорите, многое было сделано для уменьшения количества избыточных экземпляров. - person justin; 19.07.2013

Как я определяю глобальные константы:


AppConstants.h

extern NSString* const kAppBaseURL;

AppConstants.m

#import "AppConstants.h"

#ifdef DEBUG
NSString* const kAppBaseURL = @"http://192.168.0.123/";
#else
NSString* const kAppBaseURL = @"http://website.com/";
#endif

Затем в вашем файле {$ APP} -Prefix.pch:

#ifdef __OBJC__
  #import <UIKit/UIKit.h>
  #import <Foundation/Foundation.h>
  #import "AppConstants.h"
#endif

Если у вас возникнут какие-либо проблемы, сначала убедитесь, что для параметра Precompile Prefix Header установлено значение NO.

person Piotr Tomasik    schedule 12.10.2012

Вы также можете объединить строковые константы следующим образом:

  #define kBaseURL @"http://myServer.com"
  #define kFullURL kBaseURL @"/api/request"
person Martin Reichl    schedule 25.03.2013

Я действительно думаю, что другой способ сделать это намного проще, и вы просто включите его в файлы, в которые они вам нужны, а не ВСЕ файлы, как с файлом префикса .pch:

#ifndef Constants_h
#define Constants_h

//Some constants
static int const ZERO = 0;
static int const ONE = 1;
static int const TWO = 2;

#endif /* Constants_h */

После этого вы включаете этот файл заголовка в нужный файл заголовка. Вы включаете его в файл заголовка для конкретного класса, в который вы хотите его включить:

#include "Constants.h"
person Vladimir Despotovic    schedule 07.04.2016
comment
В моем тестировании статические константы не могут использоваться в отладчике (lldb Xcode). "error: use of undeclared identifier .." - person jk7; 04.04.2020

  1. Я определяю глобальную константу в файле YOURPROJECT-Prefix.pch.
  2. #define BASEURl @"http://myWebService.appspot.com/xyz/xx"
  3. затем в любом месте проекта использовать BASEURL:

    NSString *LOGIN_URL= [BASEURl stringByAppendingString:@"/users/login"];
    

Обновлено: в Xcode 6 вы не найдете файл .pch по умолчанию, созданный в вашем проекте. Поэтому используйте файл PCH в Xcode 6, чтобы вставить файл .pch в свой проект.

Обновления: для SWIFT

  1. Создайте новый файл Swift [пустой без класса] скажите [AppGlobalMemebers]
  2. & Сразу объявить / определить члена

    Пример:

    var STATUS_BAR_GREEN : UIColor  = UIColor(red: 106/255.0, green: 161/255.0, blue: 7/255.0, alpha: 1)  //
    
    1. If you want to define the app global member in any class file say Appdelegate or Singleton class or any, declare given member above class definition
person Yogesh Lolusare    schedule 19.11.2014

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

Я использую глобальные экземпляры классов (1 или 2 на проект, если необходимо), чтобы перегруппировать доступ к основным данным или некоторые логики сделок.

Например, если вы хотите иметь центральный объект, обрабатывающий все столики в ресторане, вы создаете объект при запуске, и все. Этот объект может обрабатывать доступ к базе данных ИЛИ обрабатывать его в памяти, если вам не нужно его сохранять. Он централизован, вы показываете только полезные интерфейсы ...!

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

Несколько строк кода:

@interface RestaurantManager : NSObject
    +(id) sharedInstance;
    -(void)registerForTable:(NSNumber *)tableId;
@end 

и реализация объекта:

@implementation RestaurantManager

+ (id) sharedInstance {
    static dispatch_once_t onceQueue;

    dispatch_once(&onceQueue, ^{
        sharedInstance = [[self alloc] init];
        NSLog(@"*** Shared instance initialisation ***");
    });
    return sharedInstance;
}

-(void)registerForTable:(NSNumber *)tableId {
}
@end

для использования это действительно просто:

[[RestaurantManager sharedInstance] registerForTable: [NsNumber numberWithInt: 10]]

person Gregoire Mulliez    schedule 20.05.2013
comment
Техническое название этого шаблона проектирования - Синглтон. en.wikipedia.org/wiki/Singleton_pattern - person Basil Bourque; 26.08.2013
comment
Хранение статических данных (не статических классов) в sharedmanager - не лучшая идея. - person Onder OZCAN; 11.11.2016

У принятого ответа есть 2 слабых места. Во-первых, как указали другие, это использование #define, которое труднее отлаживать, используйте вместо этого extern NSString* const kBaseUrl структуру. Во-вторых, он определяет один файл для констант. IMO, это неправильно, потому что большинству классов не нужен доступ к этим константам или доступ ко всем из них, плюс файл может стать раздутым, если там объявлены все константы. Лучшим решением было бы разбить константы на 3 разных уровня:

  1. Системный уровень: SystemConstants.h или AppConstants.h, который описывает константы в глобальной области, к которым может получить доступ любой класс в системе. Объявите здесь только те константы, к которым должен осуществляться доступ из разных классов, не связанных между собой.

  2. Уровень модуля / подсистемы: ModuleNameConstants.h, описывает набор констант, которые типичны для набора связанных классов внутри модуля / подсистемы.

  3. Уровень класса: Константы находятся в классе и используются только им.

Только 1,2 имеют отношение к вопросу.

person nsinvocation    schedule 06.09.2016

Подход, который я использовал раньше, - это создать файл Settings.plist и загрузить его в NSUserDefaults при запуске с помощью registerDefaults:. Затем вы можете получить доступ к его содержимому с помощью следующего:

// Assuming you've defined some constant kMySettingKey.
[[NSUserDefaults standardUserDefaults] objectForKey:kMySettingKey];

Хотя я не занимался разработкой для Android, похоже, что это аналог файла ресурсов строк, который вы описали. Единственным недостатком является то, что вы не можете использовать препроцессор для переключения между настройками (например, в режиме DEBUG). Я полагаю, вы могли бы загрузить другой файл.

NSUserDefaults документация <. / а>

person Community    schedule 19.08.2011
comment
Разве это не лишнее, когда все, что вам нужно, - это константа? И еще, зачем помещать это в потенциально изменяемый файл? (Особенно, если это что-то столь же важное, как IP-адрес вашего главного сервера, без которого ваше приложение не работает). - person Cyrille; 19.08.2011
comment
Я считаю, что этот подход имеет несколько преимуществ, наиболее важным из которых является то, что ваши настройки возвращаются в правильном формате (NSString, NSNumber и т. Д.). Конечно, вы можете обернуть свои #define, чтобы сделать то же самое, но тогда их не так легко редактировать. Интерфейс редактирования plist тоже хорош. :) Хотя я согласен с тем, что вы не должны помещать туда суперсекретные вещи, такие как ключи шифрования, меня не слишком беспокоят пользователи, которые ковыряются в местах, где им быть не должно - если они сломают приложение, это их собственная вина . - person ; 19.08.2011
comment
Конечно, я согласен с вашими аргументами. Как вы говорите, я оборачиваю свои #define, чтобы вернуть правильный тип, но я привык редактировать такие файлы констант, так как я всегда учился помещать такие глобальные константы в отдельный файл констант, еще с тех времен, когда я изучал Паскаль. на старом 286 :) А что касается юзера, который везде ковыряется, то я тоже согласен, это их вина. На самом деле, это просто дело вкуса. - person Cyrille; 19.08.2011
comment
@Chris Doble: Нет, файлы ресурсов в Android не похожи на NSUserDefaults. SharedPreferences и Preferences - это Android-эквивалент NSUserDefaults (хотя и более мощный, чем NSUserDefaults). Ресурсы в Android предназначены для отделения логики от контента как для локализации, так и для многих других целей. - person mrd; 23.07.2014

Для числа вы можете использовать это как

#define MAX_HEIGHT 12.5
person Gihan    schedule 20.05.2013

Я бы использовал объект конфигурации, который инициализируется из plist. Зачем беспокоить другие классы ненужными внешними вещами?

Я создал eppz!settigns исключительно по этой причине. См. Статью Расширенный, но простой способ сохранения в NSUserDefaults для включения значений по умолчанию из plist.

введите описание изображения здесь

person Geri Borbás    schedule 17.02.2014
comment
Истинный. Wordpress почему-то не разрешает ...: / ... все равно перейдите по этой прямой ссылке blog.eppz .eu /? p = 926: D - person Geri Borbás; 22.09.2016