Best practices for how you store your keys for things like NSUserDefaults

Discussion in 'Mac Programming' started by foidulus, Sep 25, 2010.

  1. foidulus macrumors 6502a

    Jan 15, 2007
    So I'm a bit new to the Obj-C/Cocoa world and I have sort of a best practices question,

    In Java and the like, the recommended way to store constant value keys for hashmaps that have constant keys is to make a constant somehwere and use that. That way it's easier for other packages to use the same keys and everyone stays in sync.

    I am currently doing that with an Obj-C cocoa project I am working on by defining the strings as extern const in a class that basically holds all the constants. However when I go to use the strings I of course get a compiler warning about discarding qualifiers from target type. I know it's perfectly valid since the stringForKey SHOULDNT be changing my strings, but I hate compiler warnings and try to generate as few as possible.

    So what I am doing is casting them to (NSString *) whenever I use them, but this doesn't seem like the best practice either. What is the recommended way of storing all these keys?
  2. Sydde macrumors 68020


    Aug 17, 2009
    I find the easiest approach is to create a single .h file that gets imported, directly or indirectly, by everything that uses the key strings and just use

    #define aKeyString @"TheParicularKeyString"

    for each key

    (By "indirectly imported", I mean imported via other imported headers that import the constants header.)
  3. kainjow Moderator emeritus


    Jun 15, 2000
    ^ I do the same. However, I only put them in a shared file if they're used across multiple files, otherwise I stick them at the top of the file it's used in.

    And since you're getting warnings, you should post your exact code so we can see what's wrong.
  4. chown33 macrumors 604

    Aug 9, 2009
    I agree. AFAIK, Objective-C will only have one instance of each string literal, regardless of how many times it's used.
  5. JoshDC macrumors regular

    Apr 8, 2009
    I believe the way they're done in most of AppKit (instead of #define) is:

    extern NSString *const Key;
    In the .h file and:

    NSString *const Key = @"Key Value";
    In the corresponding .m

    Which gives you certain benefits such as knowing the type during autocomplete.
  6. foidulus thread starter macrumors 6502a

    Jan 15, 2007
    Yeah but thats what gives you the warning since valueForKey: expects an NSString, not a const NSString, the C compiler is very picky about that stuff.

    The #define works like a charm

    Though the one thing I did just notice is that NS(Mutable)Dictionary doesn't really care if you use constants or not, it won't flag it in the compiler, but user defaults does:

    For instance:

    const NSString *GFSAVE_SERVER_DEFAULTS_NAME_KEY=@"savedservername";
     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *saveServerName=[defaults stringForKey:GFSAVE_SERVER_DEFAULTS_NAME_KEY];
    throws a compiler warning but:

    NSMutableDictionary *burt;
    NSString *earnie=[burt objectForKey:GFSAVE_SERVER_DEFAULTS_NAME_KEY];
    compiles just without any warnings

    Maybe an oversight on apple's part?
  7. kainjow Moderator emeritus


    Jun 15, 2000
    Keys in a dictionary are of type "id", they don't have to be strings, but NSUserDefaults does expect a string, thus the warning.
  8. chown33 macrumors 604

    Aug 9, 2009
    Just wanted to point out that:
    const NSString *GFSAVE_SERVER_DEFAULTS_NAME_KEY=@"savedservername";
    is not the same as:
    NSString * const GFSAVE_SERVER_DEFAULTS_NAME_KEY=@"savedservername";
    The latter is what JoshDC posted.

    The former effectively says "GFSAVE_SERVER_DEFAULTS_NAME_KEY is a pointer to a const NSString". Perhaps surprisingly to you, this does not prevent the pointer variable from being reassigned. The latter says "GFSAVE_SERVER_DEFAULTS_NAME_KEY is a constant pointer to an NSString". This does prevent reassignment. For "NSString", you can roughly interpret it as "an opaque typedef'ed struct named NSString".

    Note that Objective-C uses the const keyword identically to C. That means you can consult a C language reference doc to learn precisely how const is used in C, and exactly the same rules apply in Objective-C.

    Furthermore, you can see for yourself what's in the Cocoa headers. Example command-lines:
    cd /System/Library/Frameworks/Foundation.framework/Headers/ 
    grep const *.h | grep NSString
    sample output:
    NSUserDefaults.h:FOUNDATION_EXPORT NSString * const NSCurrencySymbol;
    NSUserDefaults.h:FOUNDATION_EXPORT NSString * const NSDecimalSeparator;
    NSUserDefaults.h:FOUNDATION_EXPORT NSString * const NSThousandsSeparator;
    NSUserDefaults.h:FOUNDATION_EXPORT NSString * const NSDecimalDigits;
    Code demonstration:
    #import <Foundation/Foundation.h>
    const NSString * KEY1=@"key1";
    NSString * const KEY2=@"key2";
    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    	NSLog( @"KEY1 = %@, KEY2 = %@", KEY1, KEY2 );
    	KEY1 = (const NSString*) @"devious";
    	NSLog( @"KEY1 = %@, KEY2 = %@", KEY1, KEY2 );
    //	KEY2 = @"does not compile";
    	NSLog( @"KEY1 = %@, KEY2 = %@", KEY1, KEY2 );
        [pool drain];
        return 0;
    2010-09-25 11:30:26.704 a.out[19653] KEY1 = key1, KEY2 = key2
    2010-09-25 11:30:26.704 a.out[19653] KEY1 = devious, KEY2 = key2
    2010-09-25 11:30:26.704 a.out[19653] KEY1 = devious, KEY2 = key2
  9. Catfish_Man macrumors 68030


    Sep 13, 2001
    Portland, OR
    The reason for using extern const strings instead of #define'd ones, btw, is that they will share storage across compilation modules. Pretty unimportant for most app code, but handy for frameworks.
  10. foidulus thread starter macrumors 6502a

    Jan 15, 2007
    Ultimately though, thanks to the posters on this forum I know that its not really necessary to declare them const, technically it prevents the memory pointed to by the object(but not the pointer itself) to be protected, but since NSStrings are immutable anyhow that really doesn't do a whole lot of anything.

    BTW, I ultimately decided just to knock the const off, no difference in the code and no more warnings. Thanks!

Share This Page