Java Developer’s Guide to String Constants in Objective-C

It is my experience that, when developing a J2ME application, excessive String creation is often the leading cause of memory “leaks”. There is always that step during the development cycle where you go back over all your code and refactor out any strings that should be defined as constants. This is no less true of Objective-C and iPhone development. What was not obvious to me initially, was what the correct method was to accomplish the equivalent this line of Java in Objective-C:

1
public static final String HTTP_METHOD_POST = "Post";


Definitely Don’t Use #define Macros

My first assumption was to use the #define macro construct to define the string which would look something like this a header (*.h) file …

1
#define HTTP_METHOD_POST @"POST"

… and would be used in the implementation file something like this …

1
[urlRequest setHTTPMethod: HTTP_METHOD_POST];

Although this would work fine, it doesn’t solve the main issue of reducing the number of instances of HTTP_METHOD_POST being created. Macros replace before the code is compiled, so the end result is effectively:

1
[urlRequest setHTTPMethod: @"POST"];

… which is right back where we started.

Public String Constants Using ‘extern’

The correct way to define a string constant is to define the string as an extern NSString *const. Here is how you would declare the constant in your header file:

1
extern NSString *const HTTP_METHOD_POST;

… and in the implemenation file (*.m) …

1
NSString *const HTTP_METHOD_POST = @"POST";

In my code I have started to always define a class that is specifically designed hold all my application’s global constants. To use the constants, you need only include the header file of the constants file.

Private String Constants Using ‘static’ (Optional)

Not all string constants need to be accessible publicly. That is to say, they are only ever used by a single class. For these classes. Our example above might be better declared in Java as:

1
private static final String HTTP_METHOD_POST = "POST";

… in Objective-C you can handle this by declaring the string as a static variable in your implementation file (*.m) like this:

1
2
3
4
5
static NSString *const HTTP_METHOD_POST = @"POST";
 
@implementation
 // method and stuff here
@end

When declaring a variable using the static modifier, it is not necessary to define anything in the header; which makes sense because it is a private constant.

17 Comments

  1. Interesting article Rodney.

    I think all of your points are correct, and wanted to add a comment. In this case, when you declare

    NSString* const SOME_STRING = @”some string”;

    What is really happening is that the “pointer” (the NSString*) is being declared as “const” (ie, so the pointer cannot be “re-pointed”), and because NSString is immutable, effectively you have locked down the whole thing. So, that is somewhat like a static final String in Java (as you say).

    As a finer point of detail, if you were to do this:

    char* const cstring = “some c string”;

    You’d get a const pointer to a non-const, null terminated array of chars (because a C string is just a sequence of chars in memory). With that declaration you could do this:

    cstring[0] = ”;

    Which would blast over the first char in the array. A truly const C string would be declared as:

    const char* const constString = “some more c string”;

    Which is declaring a const pointer to a const array of chars.

    However, it is not appropriate to do:

    const NSString* const nsstring = @”some ns string”;

    This is because where NSString* is used as a parameter (NSLog() for example), it is specified without const, and it is bad form to cast away const-ness.

    There are mutable string types (NSMutableString) and a const pointer to one of these is still mutable, so:

    NSMutableString* const mutableString = @”a mutable string”;
    [mutableString setString:@”a new string”];

    Again, I think this has parallels to Java.

    So, nothing groundbreaking in my comments, just got the metal gears whirling this Friday morning!

  2. I’m not sure that your assumption about using #define NSString constants creating more storage is necessarily correct. According to and other sources, NSString constants (@””) that contains the same string are actually references to the same allocation of that string in memory. Since #define is simply a pre-processor substitution of the NSString literal, it would not, of itself, create new instances of the string constant, but instead all uses of the defined symbol would refer to the same immutable string in memory. Thus in your example HTTP_METHOD_POST would always refer to the same string instance, regardless of how many times it was used in the code. If your goal is to reduce the number of strings constants in memory, using NSString literals is the way to go. The advantage of #define is to prevent typos from inadvertently creating a second string, for example:
    [aDict setObject: anObj forKey: @”session”];
    where later in the code:
    session = [aDict objectForKey: @”sessoin”];
    which may be difficult to detect without extensive debugging.
    Creating ‘#define SESSION_KEY @”session”‘ and using the defined symbol instead of the string literal lets the compiler preprocessor pick up spelling errors and makes the code less prone to such errors. Of course, this is a compiler optimization so is not 100% guaranteed, but for most applications, there is little need to create static data for string literals.

  3. @Steve,

    Can you provide us with the sources you mentioned? I don’t agree and here is why …

    I just put together the following test:

    NSString *str1 = @”Here is a string literal”; // <——— string literal 1
    NSLog(@”This is the Addres for the string1: %d”, &str1);

    NSString *str2 = @”Here is a string literal”; // <——— string literal 2 same as 1
    NSLog(@”This is the Addres for the string2: %d”, &str2);

    If what you say is correct then the address for both would be the same … but if you run the test you will see that they are in fact different.

  4. What your example is looking at are the addresses of the local stack variables str1 and str2, not the objects they point to. This example shows what I’m referring to:

    NSString *s1 = @”Hello, World!”;
    NSString *s2 = @”Hello, World!”;
    NSLog(@”&s1 = 0x%08lx, s1 = 0x%08lx”, (NSInteger) &s1, (NSInteger) s1);
    NSLog(@”&s2 = 0x%08lx, s2 = 0x%08lx”, (NSInteger) &s2, (NSInteger) s2);

    which produces the output:

    […] &s1 = 0xbfff6408, s1 = 0x00002030
    […] &s2 = 0xbfff6404, s2 = 0x00002030

    As seen here, the local variables on the stack have different addresses, as expected, but these are only references to the actual NSString object. So multiple uses of the same NSString literal actually refer to the same object at 0x00002030 within the same process space.

  5. That looks correct.

    It might help to understand this if we could see what @”some literal” actually translates to in the code. Presumably this maps to some instance of a specialization of NSString. I have not looked this up, but is it as simple as the @ being a special macro? Do you know off-hand how that mapping occurs?

  6. @Stevebert,

    Thanks for the response. I stand corrected. It is interesting to see how different ObjC is compared to other languages. My experience is with J2ME/Java and CF/C#; in both of those platforms, String literals “add up” and should always be avoided. This is, yet again, an example of how Objective-C excels as a language for mobile application development.

    I’ll update the article to include your feedback. Thanks again!
    – Rodney

  7. I have no special insight into the workings of the compiler, but I think the compiler detects the use of @”” string constants and makes instances of an “NSString-like” object in the DATA section of the executable (note the low-address reference for the string). I say NSString-like because this type of object can never be sent retain or release messages, so is probably not a 1st class NSString instance. Repeated uses of the string are probably matched by hashing the string literal and matching to an existing instance of the constant.

  8. so whats the conclusion..whats the better way to declare NSString constants across the application ?

    ———————————————————————–
    I created a .h file and it contents look like

    //myconstants.h
    #define stringglobal1 @”ssss”
    #define string2 @”zzzz”

    #define kMywidth 123

    and in other files I am using #import myconstants.h
    ———————————————————————–

    is it okay ?

  9. so whats the conclusion..whats the better way to declare NSString constants across the application ?

    ———————————————————————–
    I created a .h file and it contents look like

    //myconstants.h
    #define stringglobal1 @”ssss”
    #define string2 @”zzzz”

    #define kMywidth 123

    //more than 50 constats are there
    and in other files I am using #import myconstants.h
    I am also importing this header file only for using a single constant
    ———————————————————————–

    is it okay ?

  10. There’s nothing magic about #defines so including them all in a massive .h is fine. The pre-processor will substitute the string constants and the compiler will correlate multiple instances of the same string constant.

  11. You’re not incurring any more expense importing 999 unused #defines just to get that one string constant. The compiler does not incur any storage allocation just because a #define exists, it has to be used in the code being compiled and then it’s only a pointer reference to a global DATA item where the string constant is stored.

    Of course if you’re using the same .h file across multiple executables, of course each binary will contain a copy of the string constant, but you knew that already. ;-)

  12. @Rodney

    >> My experience is with J2ME/Java and CF/C#; in both of those platforms, String literals “add up” and should always be avoided.

    I know this is an old post, but I came across it while googling for ‘constants in objective c’. I can’t speak for the other environments, but this is not true of Java.

    String x = “xxx”;
    String y = “xxx;

    will result in only one String object being created. Both x and y will reference the same String object.

  13. static NSString *const HTTP_METHOD_POST = @”POST”;

    that throws
    static declaration of HTTP_METHOD_POST follows non-static declaration…

    Can any of you suggest why i am getting that error.

    • It’s hard to say why you are receiving the error without seeing the rest of your code. Have you previously defined the variable? Where are you declaring the variable, in a method, as an instance variable? You may cut/paste the definition elsewhere to see if you can narrow down where the error is occurring.

Comments are closed.