6

I have this code (small piece from a larger portion - just the troublesome part shown here):

#define kSizeLarge @"large"

-(void)determineBestFileSizeWithLimit:(int)limit
{
    static NSString *largeName = kSizeLarge;
    static NSArray *nameArray = @[kSizeLarge];
    ...
}

The compiler loves the first static variable and hates the second one, saying

Initializer element is not a compile-time constant

Removing the static from the second line makes the compiler happy.

What am/was I doing wrong or not getting correctly?

Cubs Fan Ron
  • 687
  • 6
  • 17
  • 2
    Related: http://stackoverflow.com/a/2436730/716216 – Mick MacCallum Oct 30 '12 at 14:41
  • 1
    Ok, I get it now: those collection static initializers are really just shorthand for the code does the equivalent - `@[a,b,c]` is really just shorthand for `[NSArray arrayWithObjects:a,b,c,nil]`, and since that's not a compile-time constant, the shorthand version isn't either. – Cubs Fan Ron Oct 30 '12 at 19:35
  • @CubsFanRon: not exactly. array literals are equivalent to a call to the `arrayWithObjects:count:` method – user102008 Apr 21 '13 at 04:34

2 Answers2

8

When the initializer of your static variable is not a compile-time constant, you need to use another initialization mechanism, such as dispatch_once:

-(void)determineBestFileSizeWithLimit:(int)limit {
    static NSString *largeName = kSizeLarge;
    static NSArray *nameArray = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        nameArray = @[kSizeLarge];
    });
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • @Joe Yes, that's what happens when everyone models their code after Apple's guidelines :) I keep copying the `dispatch_once` snippet from one of my [earlier answers](http://stackoverflow.com/a/13099886/335858), so `onceToken` stays the same. This particular question is a "speed typing contest", though :) – Sergey Kalinichenko Oct 30 '12 at 14:54
  • Thanks. It feels like it should be a compile-time constant because everything _is_ both constant and known at compile-time, but I guess it isn't. I was trying to avoid the extra code to manage it's static-ness (that's a word?). – Cubs Fan Ron Oct 30 '12 at 19:29
  • Oh, I marked this one as the "answer" only because it was first, not because it's any more or less right than the other response (as they're the same). – Cubs Fan Ron Oct 30 '12 at 19:33
  • @CubsFanRon Actually mine was first, only by seconds but this is fine :) – Joe Oct 30 '12 at 19:41
  • Doesn't this mean that every time this method is called, it must perform a check against the `onceToken`? – user102008 Apr 21 '13 at 04:41
  • @user102008 Yes, that is what dispatch_once will be doing. – Sergey Kalinichenko Apr 21 '13 at 10:47
4

NSArray literals are not compile-time constants as you have discovered. You should use dispatch_once to initialize the array.

#define kSizeLarge @"large"

-(void)determineBestFileSizeWithLimit:(int)limit
{
    static NSString *largeName = kSizeLarge;
    static NSArray *nameArray = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        nameArray = @[kSizeLarge];
    });
    ...
}
Joe
  • 56,979
  • 9
  • 128
  • 135
  • I don't really dig the "dispatch_once" model - I don't find it to be as clear as say `if (!nameArray) {nameArray = @[@kSizeLarge];}`, for example – Cubs Fan Ron Oct 30 '12 at 19:31
  • 2
    That is not thread safe, and the `dispatch_once` only adds 1 more line of code than what you prefer. Anyone familiar with Objective-C should have basic knowledge of GCD and blocks, if they don't yet the should learn. – Joe Oct 30 '12 at 19:33
  • 1
    I'm not really concerned lines of code as a metric - I'm more concerned about "meaning of the code" as a meme. My code is simple and direct, but since it has this latent defect waiting to bite me in the a**, I think I'll learn a new meme. Then this new code will become simple and direct in my head, which is something I do care about. – Cubs Fan Ron Oct 31 '12 at 20:51
  • Let me further test my understanding: if I have a static variable, either limited to the file scope or a function/method scope, if it has a non-compile-time value I should use the `dispatch_once` approach to initialize it. Should I also be doing this for non-static variables (e.g., properties), particularly those that are created via lazy instantiation? – Cubs Fan Ron Oct 31 '12 at 20:52
  • 1
    No, do not use this for non-static variables because they would only get assigned on the first run. You are correct that you should use this for initializing any static non-compile-time constants. – Joe Oct 31 '12 at 22:10
  • Thanks for the help, I think I'm seeing this more clearly. It is convenient that Xcode comes with a pre-installed snippet for this meme. – Cubs Fan Ron Nov 01 '12 at 02:30
  • Just a brief follow-up triggered by another answer contribution: I now dig the "dispatch_once" model after using it for a bit. It makes it so I no longer have to think about race condition potential - it will be done exactly once and now I can move on to different problems. I wish I could up-vote you again. :) – Cubs Fan Ron Feb 13 '14 at 18:07