Constants in Objective-C

Constants in Objective-C

You should create a header file like:

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

(You can use extern instead of FOUNDATION_EXPORT if your code will not be used in mixed C/C++ environments or on other platforms.)

You can include this file in each file that uses the constants or in the pre-compiled header for the project.

You define these constants in a .m file like:

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

Constants.m should be added to your application/framework's target so that it is linked in to the final product.

The advantage of using string constants instead of #define'd constants is that you can test for equality using pointer comparison (stringInstance == MyFirstConstant) which is much faster than string comparison ([stringInstance isEqualToString:MyFirstConstant]) (and easier to read, IMO).

What is the best way to create constants in Objective-C

The first question is what scope you want your constants to have, which is really two questions:

  • Are these constants specific to a single class, or does it make sense to have them all over the application?
  • If they are class-specific, are they for use by clients of the class, or only within the class?

If they are specific and internal to a single class, declare them as static const at the top of the .m file, like so:

static NSString *const MyThingNotificationKey = @"MyThingNotificationKey";

If they pertain to a single class but should be public/used by other classes, declare them as extern in the header and define them in the .m:

//.h
extern NSString *const MyThingNotificationKey;

//.m
NSString *const MyThingNotificationKey = @"MyThingNotificationKey";

If they should be global, declare them in a header and define them in a corresponding module, specifically for those constants.

You can mix and match these for different constants with different levels of how global you want them to be, and for different global constants that simply don't belong together—you can put them in separate modules, each with its own header, if you want.

Why not #define?

The old answer is “macros don't have type information”, but compilers today are pretty smart about doing all the type-checking for literals (what macros expand to) as well as variables.

The modern answer is because the debugger won't know about your macros. You can't say [myThing addObserver:self forKey:MyThingNotificationKey] in a debugger command if MyThingNotificationKey is a macro; the debugger can only know about it if it is a variable.

Why not enum?

Well, rmaddy beat me to it in the comments: enum can only define integer constants. Things like serial identifier numbers, bit-masks, four-byte codes, etc.

For those purposes, enum is great and you absolutely should use it. (Even better, use the NS_ENUM and NS_OPTIONS macros.) For other things, you must use something else; enum does not do anything but integers.

And other questions

I was thinking about importing the file in the Reddit-Prefix.pch file to make the constants available to all the files. Is it a good way of doing things?

Probably harmless, but probably excessive. Import your constants header(s) where you need them.

What are the use cases for each of those solutions?

  • #define: Pretty limited. I'm honestly not sure there's a good reason to use this for constants anymore.
  • const: Best for local constants. Also, you have to use this for one you declared in a header and are now defining.
  • static const: Best for file-specific (or class-specific) constants.
  • extern const: You must use this when exporting a constant in a header.

Also, if using extern const, do I need to import the file, or the constants will be available globally without importing the file?

You need to import the file, either in each file where you use it or in the prefix header.

Class with defined static constant values in Objective-C

You could do it using preprocessors:

#define API_URL @"http://api.service.com"
#define SOME_VALUE (7)

Accessing defines would be simple: [object do:API_URL];

Or you could use constants

NSString * const apiURL = @"http://api.service.com";
NSNumber * const someValue = @7;

Accessing consts would be like accessing variables, So the string would just be a simple call. The NSNumber is an object wrapper for primitives so you'd need to access it like: someValue.intValue

Where do you declare a constant in Objective-C?

You can declare in the header, define it in a code file. Simply declare it as

extern const double EARTH_RADIUS;

then in a .m file somewhere (usually the .m for the .h you declared it in)

const double EARTH_RADIUS = 6353;

#define vs const in Objective-C

First, I found that its not possible to define the type of the constant using #define, why is that?

Why is what? It's not true:

#define MY_INT_CONSTANT ((int) 12345)

Second, are there any advantages to use one of them over the another one?

Yes. #define defines a macro which is replaced even before compilation starts. const merely modifies a variable so that the compiler will flag an error if you try to change it. There are contexts in which you can use a #define but you can't use a const (although I'm struggling to find one using the latest clang). In theory, a const takes up space in the executable and requires a reference to memory, but in practice this is insignificant and may be optimised away by the compiler.

consts are much more compiler and debugger friendly than #defines. In most cases, this is the overriding point you should consider when making a decision on which one to use.

Just thought of a context in which you can use #define but not const. If you have a constant that you want to use in lots of .c files, with a #define you just stick it in a header. With a const you have to have a definition in a C file and

// in a C file
const int MY_INT_CONST = 12345;

// in a header
extern const int MY_INT_CONST;

in a header. MY_INT_CONST can't be used as the size of a static or global scope array in any C file except the one it is defined in.

However, for integer constants you can use an enum. In fact that is what Apple does almost invariably. This has all the advantages of both #defines and consts but only works for integer constants.

// In a header
enum
{
MY_INT_CONST = 12345,
};

Finally, which way is more efficient and/or more secure?

#define is more efficient in theory although, as I said, modern compilers probably ensure there is little difference. #define is more secure in that it is always a compiler error to try to assign to it

#define FOO 5

// ....

FOO = 6; // Always a syntax error

consts can be tricked into being assigned to although the compiler might issue warnings:

const int FOO = 5;

// ...

(int) FOO = 6; // Can make this compile

Depending on the platform, the assignment might still fail at run time if the constant is placed in a read only segment and it's officially undefined behaviour according to the C standard.

Personally, for integer constants, I always use enums for constants of other types, I use const unless I have a very good reason not to.

Global constants in Objective-C

Solution A:

Personally, I would just use a function for ANOTHER_URL.

Solution B:

If you really want a constant: You should be able to use cstring concatenation rules via #define, then pipe that through CFSTR():

// defs.h
extern NSString * const BASE_URL;
extern NSString * const ANOTHER_URL;

// defs.m

#ifdef DEBUG
#define DEF_BASE_URL "http://www.example.org"
#else
#define DEF_BASE_URL "http://localhost"
#endif

NSString * const BASE_URL = (NSString*)CFSTR(DEF_BASE_URL);
NSString * const ANOTHER_URL = (NSString*)CFSTR(DEF_BASE_URL "/path/");

Solution C:

If you want to create just one via initialization, you can also accomplish a function/method local static in C++/ObjC++ translations (then use C or ObjC visibility, where needed):

NSString * URL() {
static NSString * const ANOTHER_URL = [NSString stringWithFormat:@"%@%@", BASE_URL, @"/path/"];
return ANOTHER_URL;
}

How to declare constants

The answer depends on the type of your constant. If all you need is an int or a double, you can use preprocessor and the #define CONST 123 syntax. For Objective C classes, however, you need to do a lot more work.

Specifically, you would need to hide the constant behind a class method or a free-standing function. You will also need to add a prototype of that method or function in the header file, provide a function-scoped static variable to store the constant, and add code to initialize it.

Here is an example using a simple NSDictionary:

Header: MyConstants.h

@interface MyConstants
+(NSDictionary*)getConstDictionary;
@end

Implementation: MyConstants.m

+(NSDictionary*)getConstDictionary {
static NSDictionary *inst = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
inst = @{
@"key1": @"value1",
@"key2": @"value2",
@"key3": @"value3"
};
});
return inst;
}

Usage:

NSString *val = [[MyConstants getConstDictionary] objectForKey:@"key2"];

Objective-C: how to group a series of string constants?

Here's one approach:

MONExtResult.h

// add __unsafe_unretained if compiling for ARC
struct MONExtResultStruct {
NSString * const AppID;
NSString * const ErrorCode;
NSString * const Progress;
};

extern const struct MONExtResultStruct MONExtResult;

MONExtResult.m

const struct MONExtResultStruct MONExtResult = {
.AppID = @"appid",
.ErrorCode = @"errorcode",
.Progress = @"progress"
};

In use:

NSString * str = MONExtResult.AppID;


Related Topics



Leave a reply



Submit