Constants in Objective-C

If you’re a Java programmer developing on the iPhone platform, you’ve probably wondered about how to set constants in your programs. Undoubtedly, you have have come across the #define preprocessor macro, and maybe a few other methods, but I’m going to show you my approach to this problem using the singleton design pattern.

Java programmers, like all mere mortals, are used to defining their constants as static constant variable accessing it throughout the application, like so:

public class Constants {
    public static final int RED = 1;
    public static final int BLUE = 2;

    public static void main (String[] args) {
        System.out.println(Constants.RED);
    }
}

Objective-C doesn’t have an equivalent. There is the #define preprocessor macro, but those need to be defined in each file (and cannot be imported), so are very impractical. For integer constants, you can use typedef enums in your header file, as described by the first commenter in this thread. But if you need NSString constants, we need a different method.

Singleton Pattern

A Singleton Design Pattern is a way to define a class so that it can only have one instance, and no more. Why is this useful? Because we can define our variables in a singleton class and just import the file. I created my own Constants class much like Wikipedia’s Singleton class – in retrospect, I should have done it this way, and will do it in the future.

Once you have your singleton class, you define your variables in the overridden init method, synthesize the get methods, set the @property to (readonly) to only generate the get accessor. Here is my example:

//===== Constants.h ================
@interface Constants : NSObject
{
    // Place any "global" variables here
    int RED;
    int BLUE;
}
// message from which our instance is obtained
+ (Constants *)sharedInstance;
- (void) setConstants;
@property (readonly, nonatomic) int RED;
@property (readonly, nonatomic) int BLUE;
@end

//===== Constants.m ================
@implementation Constants

@synthesize RED, BLUE;

+ (Constants *)sharedInstance
{
    // the instance of this class is stored here
    static Constants *myInstance = nil;

    // check to see if an instance already exists
    if (nil == myInstance) {
        myInstance  = [[[self class] alloc] init];
        [myInstance setConstants];
    }
    // return the instance of this class
    return myInstance;
}

/*Set constants here, instead of +sharedInstance method
because non-static variables are not accessible from static
methods.  You can also overload the init constructor
(I prefer it this way because it's more readable)*/
- (void) setConstants {
    RED = 0;
    BLUE = 1;
}

@end

The +sharedInstance method is a static method that checks to see if the Constants class has been instantiated. If it hasn’t, it makes an instance, and sets all the variables to what you want them to be, and returns the singleton instance. If it IS instantiated, then it just returns the previously instantiated instance.

Also, the readonly property only takes effect if you try to access it via a setter method. However, you’ll notice we can still directly access variables in the setConstants method because these variables are part of the class (effectively, they are private variables now). In Java terms, this would be like creating private variables and setting them within the object, but not creating the setter methods.

To use the constants, you would simply import Constants.h, and do this:
int red = [Constants sharedInstance].RED

I’m not 100% happy with this method because there are a few nasty things that can happen if you misuse this class, but in general it should be ok. Some things that can go wrong:

  • Instantiating a Constants class yourself: [[Constants alloc] init]. The variable initialization code would never get called, and then you would wonder why your Constants are not defined somewhere in your code. Don’t do this! Alternatively, you could set all your variables in the init method, and not really have to worry about this, but why? Just used the sharedInstance method, and you’re ok.
  • You could re-set your constant variables (either by accident, or on purpose). For example, the code if ([Constants sharedInstance].RED = 3) is a common mistake. This sets RED to 3… forever. Make sure that the property is set to readonly.

There you have it. I am definitely interested to see how other people solved this problem, and if this method worked for you.

Leave a Reply