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

111

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

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

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

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

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

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

JoJo
источник

Ответы:

145

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

#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
Сирил
источник
Я использую "constants.h"подход, объявляя staticпеременные на основе #ifdef VIEW_CONSTANTS ... #endif. Таким образом, у меня есть один файл констант для всего приложения, но каждый из других моих файлов кода #defineсодержит разные наборы констант, которые нужно включить перед #includeвключением файла констант (останавливает все эти предупреждения компилятора "определенные, но не используемые").
2
При использовании этого решения я столкнулся с двумя проблемами. Во-первых, когда я использовал #decalare, у меня была ошибка компиляции: « Объявление недопустимой директивы предварительной обработки ». Поэтому я заменил его на #define. Другая проблема - использование константы. Я хотел создать другую константу с static NSString* const fullUrl = [NSString stringWithFormat:@"%@%@", kbaseUrl, @"script.php"], но, очевидно, создавать константы с выражением незаконно. Я получаю сообщение об ошибке « элемент инициализатора не является постоянным ».
JoJo
1
@Cyrille Android действительно интересно практиковать, есть некоторые возможности, которые вы не могли себе представить на iOS! В любом случае спасибо за ответ
klefevre
8
По возможности предпочитайте константу #define - вы получите лучшую проверку во время компиляции, и отладка будет работать лучше.
occulus
2
@AnsonYao обычно, когда такое случается со мной, я забываю убрать точку с запятой из #define, например #define kBaseURL @"http://192.168.0.123/";
Gyfis
168

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

Кроме того, предпочтительнее просто объявить 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это хорошо.

джастин
источник
26
Не уверен, почему в принятом ответе 40 голосов за поддержку #define - const действительно лучше.
occulus
1
Определенно const NSString лучше, чем #define, это должен быть принятый ответ. #define создает новую строку каждый раз, когда используется определенное значение.
jbat100
1
@ jbat100 Я не думаю, что он создает новую строку. Я думаю, что компилятор определяет, создает ли ваш код одну и ту же статическую строку 300000 раз, и создаст ее только один раз. @"foo"не то же самое, что [[NSString alloc] initWithCString:"foo"].
Abhi Beckert
@AbhiBeckert я думаю, что jbat пытался сделать то, что можно получить дубликаты вашей константы, когда #defineона используется (т.е. равенство указателя может потерпеть неудачу) - а не то, что буквальное выражение NSString создает временное при каждом выполнении.
Джастин
1
Я согласен с тем, что #define - плохая идея, я просто хотел исправить сделанную им ошибку, заключающуюся в создании нескольких объектов. Кроме того, нельзя полагаться на равенство указателей даже для констант. Он может быть загружен из NSUserDefaults или чего-то еще. Всегда используйте isEqual :.
Abhi Beckert
39

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


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.

Петр Томасик
источник
5

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

  #define kBaseURL @"http://myServer.com"
  #define kFullURL kBaseURL @"/api/request"
Мартин Райхль
источник
4

Я думаю, что другой способ сделать это намного проще, и вы просто включите его в файлы, в которые они вам нужны, а не ВСЕ файлы, как в случае с файлом префикса .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"
Владимир Деспотович
источник
В моем тестировании статические константы не могут использоваться в отладчике (lldb Xcode). "error: use of undeclared identifier .."
jk7,
3
  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. Если вы хотите определить глобальный член приложения в любом файле класса, например, Appdelegate или Singleton class или любом другом, объявите данный член над определением класса
Йогеш Лолусаре
источник
2

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

Я использую глобальные экземпляры классов (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]]

Грегуар Муллиес
источник
3
Техническое название этого шаблона проектирования - Singleton. en.wikipedia.org/wiki/Singleton_pattern
Бэзил Бурк,
Хранение статических данных (не статических классов) в sharedmanager - не лучшая идея.
Ондер ОЗКАН
1

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

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

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

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

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

Стефан
источник
0

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

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

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

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

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

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

#define MAX_HEIGHT 12.5
Gihan
источник
0

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

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

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

Гери Борбас
источник
Правда. Wordpress почему-то не разрешает ...: / ... все равно перейдите по этой прямой ссылке blog.eppz.eu/?p=926 : D
Geri Borbás