Search code examples
iosobjective-cstaticnsdictionaryconstants

static NSDictionary* const letterValues = @{ ..... } in a method does not compile


In a word game for iPhone:

app screenshot

I'm trying to use the following code in my custom view Tile.m:

- (void)awakeFromNib
{
    [super awakeFromNib];

    static NSDictionary* const letterValues = @{
                                         @"A": @1,
                                         @"B": @4,
                                         @"C": @4,
                                         // ...
                                         @"X": @8,
                                         @"Y": @3,
                                         @"Z": @10,
                                         };

    NSString* randomLetter = [kLetters substringWithRange:[kLetters rangeOfComposedCharacterSequenceAtIndex:arc4random_uniform(kLetters.length)]];
    int letterValue = [letterValues[randomLetter] integerValue];

    _smallLetter.text = _bigLetter.text = randomLetter;
    _smallValue.text = _bigValue.text = [NSString stringWithFormat:@"%d", letterValue];
}

Unfortunately this gives me compile error Initializer element is not a compile-time constant and I have to remove the static keyword to get my app compile in Xcode (here fullscreen):

Xcode screenshot

I think I initialize the NSDictionary correctly - by using the new Objective-C Literals syntax.

But why can't I use static here?

I thought it would be appropriate here to make sure that my letterValues constant is set only once?


Solution

  • You can only set a static variable during initialization with a constant. @{} creates an object, thus not a constant.

    Do this instead:

    - (void)awakeFromNib
    {
        [super awakeFromNib];
    
        static NSDictionary* letterValues = nil;
    
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            letterValues = @{
              @"A": @1,
              @"B": @4,
              @"C": @4,
              // ...
              @"X": @8,
              @"Y": @3,
              @"Z": @10,
              };
        });
    
    
        ...
    }
    

    Some other answers here suggest a check for nil instead of dispatch once, but that can cause issues when creating multiple tiles at the same time (via threads). dispatch_once implements the required locking.