views:

308

answers:

2

I see a lot of objective-c code that just #defines local constants it needs, and then proceeds on its merry way. The problem is that, as far as I know, #defines aren't scoped. Many of this is in Apple's own example code. For example, in the TableViewSuite example 5, the drawRect function in TimeZoneView.m contains the following block:

#define LEFT_COLUMN_OFFSET 10
#define LEFT_COLUMN_WIDTH 130

#define MIDDLE_COLUMN_OFFSET 140
#define MIDDLE_COLUMN_WIDTH 110

#define RIGHT_COLUMN_OFFSET 270

#define UPPER_ROW_TOP 8
#define LOWER_ROW_TOP 34

#define MAIN_FONT_SIZE 18
#define MIN_MAIN_FONT_SIZE 16
#define SECONDARY_FONT_SIZE 12
#define MIN_SECONDARY_FONT_SIZE 10

Is there some reason I don't understand that this is not absurdly dangerous? At a very minimum, shouldn't we #undef these constants at the end of the function?

That's my question I suppose:

Is it a better practice to define what you need in the file you need it, and un-define it at the end? Or do you think it's better to just use static consts for this type of thing? Is there any performance penalty to using static consts, or is the compiler able to handle them just as efficiently as #define?

+4  A: 

#defines in implementation files (.m) are by definition scoped to the file they're in, since no one else #includes a .m file. (You do want to think carefully about this in common header files, where the scoping issue you mention is real, and SO_QUESTION_2345197_NAMESPACE_YOUR_CONSTANTS_APPROPRIATELY.)

For local constants in implementation files which is what you seem to be asking about, #define is more efficient to compile, but you don't get the symbols when you debug. Local consts have that benefit, and in some cases (string constants? maybe? depends) prevent duplication of constant data in the binary, although at this point in the world, size and compile efficiency (and runtime efficiency to look them up) is basically noise unless you profile some tight loop and find an issue with it.

quixoto
Cool, thanks Ben, I think that answers that. I do agree that it's probably trivial, but trivial things can add up and, all other things being equal, I prefer to get in the habit of using performant conventions by default. That way, when you do encounter a case where it matters, you're already in the habit of doing it.
DougW
String constants i.e. those defined with @"" are singleton/atoms and they do say quite a bit of memory and time. NSNumbers are the same way. In any given process there is only one instance generated by [NSNumber numberWithInt:1].
TechZen
NSNumber interns a small subset, not the entire range of integers whereas constant NSStrings will always be interned
rpetrich
Good answer. Also, I'd like to add that the use of enum is also a good way of defining numeric constants, both in header files and in implementation files, since proper scoping rules (i.e. the names can be overshadowed) will apply.
Michael Aaron Safyan
+4  A: 

Of late, I've started using class methods to store constants. I started it as a hack to store the key names in an ungodly huge Core Data model. However, it's proved rather efficient both in the code and from a code base creation and maintenance perspective. I generate a category like so:

@interface MyClass (KeyNames)
+ (NSString *) creationDate_Key;
@end

@implementation MyClass (KeyNames)

+ (NSString *) creationDate_Key{
    return @"creationDate";
} 
@end

Then I use it like:

NSString *key=[MyClass creationDate_Key];

I have a script that generates the methods for me. The neat thing is that they are scoped, inherited and more compact than long defines. If I need to use the key a lot, as in a loop, I just park it in a local variable if efficiency becomes an issue.

TechZen