Константы в Objective-C

Я разрабатываю какао приложение, и я использую constant NSStrings как способы хранения имен ключей для моих предпочтений.

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

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

13 ответов


вы должны создать файл заголовка, как

// Constants.h
FOUNDATION_EXPORT NSString *const MyFirstConstant;
FOUNDATION_EXPORT NSString *const MySecondConstant;
//etc.

(вы можете использовать extern вместо FOUNDATION_EXPORT Если ваш код не будет использоваться в смешанных средах C / C++ или на других платформах)

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

вы определяете эти константы в a .файл M как

// Constants.m
NSString *const MyFirstConstant = @"FirstConstant";
NSString *const MySecondConstant = @"SecondConstant";

константы.m должен быть добавлен в цель вашего приложения / фреймворка, чтобы он связан с конечным продуктом.

преимущество использования строковых констант вместо #define'D константы, что вы можете проверить на равенство с помощью сравнения указателя (stringInstance == MyFirstConstant), которая намного быстрее, чем сравнение строк ([stringInstance isEqualToString:MyFirstConstant]) (и легче читать, ИМО).


самый простой способ:

// Prefs.h
#define PREFS_MY_CONSTANT @"prefs_my_constant"

лучше так:

// Prefs.h
extern NSString * const PREFS_MY_CONSTANT;

// Prefs.m
NSString * const PREFS_MY_CONSTANT = @"prefs_my_constant";

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


есть еще одна вещь, чтобы упомянуть. Если вам нужна не глобальная константа, вы должны использовать static ключевое слово.

пример

// In your *.m file
static NSString * const kNSStringConst = @"const value";

из-за static ключевое слово, этот const не отображается вне файла.


незначительные исправления @QuinnTaylor: статические переменные видны внутри единица компиляции. Обычно это сингл .M файл (как в этом примере), но он может укусить вас, если вы объявляете его в заголовке, который включен в другом месте, так как вы получите ошибки компоновщика после компиляции


принятый (и правильный) ответ говорит, что "вы можете включить этот [константы.файл H]... в предварительно скомпилированном заголовке для проекта."

как Новичок, мне было трудно сделать это без дальнейшего объяснения - вот как: в вашем Yourappnamehere-префикс.PCH-файл (это имя по умолчанию для предварительно скомпилированного заголовка в Xcode), импортируйте константы.h внутри #ifdef __OBJC__ блок.

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

Также обратите внимание, что константы.h и константы.m-файл не должно содержать в них абсолютно ничего, кроме того, что описано в принятом ответе. (Нет интерфейса или реализации).


Я обычно использую способ, опубликованный Барри Уорком и Рахулом Гуптой.

хотя, я не люблю повторять одни и те же слова в обоих .h и .m-файл. Обратите внимание, что в следующем примере строка почти идентична в обоих файлах:

// file.h
extern NSString* const MyConst;

//file.m
NSString* const MyConst = @"Lorem ipsum";

поэтому мне нравится использовать некоторые машины препроцессора C. Позвольте мне объяснить на примере.

у меня есть файл заголовка, который определяет макрос STR_CONST(name, value):

// StringConsts.h
#ifdef SYNTHESIZE_CONSTS
# define STR_CONST(name, value) NSString* const name = @ value
#else
# define STR_CONST(name, value) extern NSString* const name
#endif

в моей .ч./м пара, в которой я хочу определить константу, я делаю следующее:

// myfile.h
#import <StringConsts.h>

STR_CONST(MyConst, "Lorem Ipsum");
STR_CONST(MyOtherConst, "Hello world");

// myfile.m
#define SYNTHESIZE_CONSTS
#import "myfile.h"

et voila, у меня есть вся информация о константах .только файл h.


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

однако, следующее изменение позволяет ему работать. Это немного запутанно, но работает.

вам понадобится 3 файлы который имеет постоянные определения, и .m файл, я буду использовать ConstantList.h, Constants.h и Constants.m, соответственно. содержание Constants.h просто:

// Constants.h
#define STR_CONST(name, value) extern NSString* const name
#include "ConstantList.h"

и выглядит так:

// Constants.m
#ifdef STR_CONST
    #undef STR_CONST
#endif
#define STR_CONST(name, value) NSString* const name = @ value
#include "ConstantList.h"

наконец,ConstantList.h файл имеет фактические объявления в нем, и это все:

// ConstantList.h
STR_CONST(kMyConstant, "Value");
…

несколько вещей, чтобы отметить:

  1. мне пришлось переопределить макрос в .m файл после #undefing это для макроса, который будет использоваться.

  2. мне также пришлось использовать #include вместо #import чтобы это работало правильно и не позволяло компилятору видеть ранее скомпилированные значения.

  3. это потребует перекомпиляции вашего PCH (и, вероятно, всего проекта) всякий раз, когда какие-либо значения изменяются, что не так, если они разделены (и дублируются) как нормальный.

надеюсь, что это полезно для кого-то.


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

extern NSString * const PPRememberMusicList;
extern NSString * const PPLoadMusicAtListLoad;
extern NSString * const PPAfterPlayingMusic;
extern NSString * const PPGotoStartupAfterPlaying;

затем объявляя их в сопроводительном .файл м:

NSString * const PPRememberMusicList = @"Remember Music List";
NSString * const PPLoadMusicAtListLoad = @"Load music when loading list";
NSString * const PPAfterPlayingMusic = @"After playing music";
NSString * const PPGotoStartupAfterPlaying = @"Go to startup pos. after playing";

этот подход сослужил мне хорошую службу.

Edit: обратите внимание, что это лучше всего работает, если строки используются в нескольких файлах. Если только один файл использует его, вы можете просто сделать #define kNSStringConstant @"Constant NSString" в рамках .M файл, который использует строку.


// Prefs.h
extern NSString * const RAHUL;

// Prefs.m
NSString * const RAHUL = @"rahul";

Как сказал Абайзер, вы можете поместить его в файл PCH. Другой способ, который не так грязен, - сделать файл включения для всех ваших ключей, а затем либо включить его в файл, в котором вы используете ключи, либо включить его в PCH. С ними в их собственном файле include, что, по крайней мере, дает вам одно место для поиска и определения всех этих констант.


Если вы хотите что-то вроде глобальных констант; быстрый грязный способ-поставить постоянные заявления в .


Я использую одноэлементный класс, чтобы я мог издеваться над классом и изменять константы, если это необходимо для тестирования. Класс constants выглядит следующим образом:

#import <Foundation/Foundation.h>

@interface iCode_Framework : NSObject

@property (readonly, nonatomic) unsigned int iBufCapacity;
@property (readonly, nonatomic) unsigned int iPort;
@property (readonly, nonatomic) NSString * urlStr;

@end

#import "iCode_Framework.h"

static iCode_Framework * instance;

@implementation iCode_Framework

@dynamic iBufCapacity;
@dynamic iPort;
@dynamic urlStr;

- (unsigned int)iBufCapacity
{
    return 1024u;
};

- (unsigned int)iPort
{
    return 1978u;
};

- (NSString *)urlStr
{
    return @"localhost";
};

+ (void)initialize
{
    if (!instance) {
        instance = [[super allocWithZone:NULL] init];
    }
}

+ (id)allocWithZone:(NSZone * const)notUsed
{
    return instance;
}

@end

и он используется так (Обратите внимание на использование стенографии для констант c-это экономит ввод [[Constants alloc] init] каждый раз):

#import "iCode_FrameworkTests.h"
#import "iCode_Framework.h"

static iCode_Framework * c; // Shorthand

@implementation iCode_FrameworkTests

+ (void)initialize
{
    c  = [[iCode_Framework alloc] init]; // Used like normal class; easy to mock!
}

- (void)testSingleton
{
    STAssertNotNil(c, nil);
    STAssertEqualObjects(c, [iCode_Framework alloc], nil);
    STAssertEquals(c.iBufCapacity, 1024u, nil);
}

@end

попробуйте использовать метод класса:

+(NSString*)theMainTitle
{
    return @"Hello World";
}

Я использую его иногда.


Если вам нравится константа пространства имен, вы можете использовать struct, Friday Q&A 2011-08-19: константы и функции пространства имен

// in the header
extern const struct MANotifyingArrayNotificationsStruct
{
    NSString *didAddObject;
    NSString *didChangeObject;
    NSString *didRemoveObject;
} MANotifyingArrayNotifications;

// in the implementation
const struct MANotifyingArrayNotificationsStruct MANotifyingArrayNotifications = {
    .didAddObject = @"didAddObject",
    .didChangeObject = @"didChangeObject",
    .didRemoveObject = @"didRemoveObject"
};