DrWurm
DrWurm

Reputation: 185

NSNumber constants in Obj-C

I want to make some NSNumber constants via the same style used for NSStrings in this topic. That is, I'm creating separate constants.h/.m files and importing them into classes that need to access them.

Upvotes: 18

Views: 11976

Answers (4)

nielsbot
nielsbot

Reputation: 16022

update:

Years later, I just realized it is possible to create a NSNumber constant for integers... but it's a hack:

#define CONST_INT_NSNUMBER( x ) ((__bridge NSNumber * const)(void * const)(( x << 8 ) | 0x27))


NSNumber * const number = CONST_INT_NSNUMBER(123) ;

This works because certain integer NSNumbers are stored as tagged pointers.


original answer:

You can't do it.

NSNumber * const mynumber = @5.5;

gives:

Initializer element is not a compile-time constant

Implying the compiler has a special feature specifically for creating compile-time constant NSString objects, but not any other type of object.

You could do this, however:

.h:

extern NSNumber * kConstantNumber ;

.m:

NSNumber * kConstantNumber ;

@implementation NSNumber (InitializeConstants)

+(void)load
{
    kConstantNumber = @42;
    // ... and the rest ...
}

@end

Upvotes: 5

David Hoerl
David Hoerl

Reputation: 41622

You can basically achieve close to what you want in three parts:

.h file:

extern NSNumber *MyFirstConstant;

.m file

NSNumber *MyFirstConstant;

AppDelegate.m

+(void)initialize
{
    MyFirstConstant = @5;
    ...
}

AppDelegate is guaranteed to run before any of your other code, and the initialize is the first method that would be called on AppDelegate, so you can essentially insure all your constants are setup for you before your app runs.

Upvotes: 5

Chuck
Chuck

Reputation: 237010

The trouble with doing this is that there isn't such a thing as a compile-time constant NSNumber. Only NSString gets that distinction. NSNumbers are always created dynamically. You can fake it by using a function that runs at your program's startup to initialize the variables. Your options:

  1. Create a class with a +load method that performs the initialization.

  2. In the file with the constants, include a function with __attribute__((constructor)). So, for example:

    // Constants.m
    
    NSNumber *someGlobalNumber;
    
    __attribute__((constructor))
    static void InitGlobalNumber() {
        someGlobalNumber = [[NSNumber numberWithInteger:1] retain];
    }
    

But of course then you can't reliably use these numbers in any other functions which are run that early in the startup process. This usually isn't a problem, but is worth keeping in mind.

The other option, which I've seen crop up a few times, is to have a class with accessors for the numbers instead of giving raw access to the variables. It's a bit of a heavier design, but it also feels less voodooish, which has its charms.

Upvotes: 18

DHamrick
DHamrick

Reputation: 8488

Unfortunately you cannot currently generate NSNumber constants in the same way you can generate NSString constants. When you try to do you will get a compiler error

NSNumber * const kNumberConstant = @2; // This doesn't work.

However, you can use primitives instead.

NSInteger const kSomeIntValue = 10;

Upvotes: 16

Related Questions