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

Robert Audi picture Robert Audi · Jun 21, 2013 · Viewed 69.8k times · Source

I am creating a Reddit client for learning purposes. I need to have a file with constants in it. 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? Also, I've done my research and found several methods to create constants, but I don't know which one to use:

  • #define macro
  • const
  • static const
  • extern const
  • enum

So which way is the preferred way? What is the convention? I know that "it depends" but my question more specifically is: What are the use cases for each of those solutions?

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

One thing I could logically conclude is that enum is the best choice when defining something like custom error domains (am I actually right?). But what about the others?

Answer

Peter Hosey picture Peter Hosey · Jun 21, 2013

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.