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

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

  1. macrumors 6502a

    Joined:
    Jan 15, 2007
    #1
    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. macrumors 68000

    Sydde

    Joined:
    Aug 17, 2009
    #2
    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. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #3
    ^ 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. macrumors 603

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

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

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

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

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

    Joined:
    Jan 15, 2007
    #6
    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:


    Code:
    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:

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

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

    kainjow

    Joined:
    Jun 15, 2000
    #7
    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. macrumors 603

    Joined:
    Aug 9, 2009
    #8
    Just wanted to point out that:
    Code:
    const NSString *GFSAVE_SERVER_DEFAULTS_NAME_KEY=@"savedservername";
    
    is not the same as:
    Code:
    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:
    Code:
    cd /System/Library/Frameworks/Foundation.framework/Headers/ 
    grep const *.h | grep NSString
    sample output:
    Code:
    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;
    
    EDIT:
    Code demonstration:
    Code:
    #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;
    }
    
    Output:
    Code:
    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. macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
    #9
    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. thread starter macrumors 6502a

    Joined:
    Jan 15, 2007
    #10
    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