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.
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:
Create a class with a +load method that performs the initialization.
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.
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;
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.
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
Related
I'm attempting to understand Objective C arrays. What I want to do is create a multidimensional NSInteger instance array.
Does the code immediately below create an array equivalent to initiating an array and running a double for loop containing NSNull at each point?
#interface Builder: UIView {
NSInteger superDuperArray[5][4];
}
How do I add/replace specific data (NSIntegers) considering this is not an NSMutableArray? I need to do so elsewhere in the file, in several different methods.
The instance variable you've declared does indeed create a multidimensional array that is accessible from any method in the object. It does not, however, contain NSNulls, because that's an object and NSInteger is a primitive type. It contains 0 in every slot (all ivars are initialized to their appropriate zero-value at the object's allocation).
If you don't need to change the size of the array, you're pretty much done. You access the array inside your object just like you'd access an array anywhere else, by subscripting: superDuperArray[2][1], e.g.
Here's a full sample:
#import <Foundation/Foundation.h>
#interface Mutli : NSObject
- (NSInteger)numberAtRow:(NSInteger)row column:(NSInteger)col;
- (void)setNumber:(NSInteger)newNum atRow:(NSInteger)row column:(NSInteger)col;
#end
#implementation Mutli
{
NSInteger numbers[5][4];
}
-(void)setNumber:(NSInteger)newNum atRow:(NSInteger)row column:(NSInteger)col
{
numbers[row][col] = newNum;
}
- (NSInteger)numberAtRow:(NSInteger)row column:(NSInteger)col
{
return numbers[row][col];
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
Mutli * m = [Mutli new];
[m setNumber:10 atRow:2 column:1];
NSLog(#"%ld", [m numberAtRow:2 column:1]);
// This may not crash!
[m setNumber:10 atRow:100 column:34];
}
return 0;
}
N.B. That last line. There's no bounds checking inherent to primitive arrays. Accesses outside the bounds you've set may not crash or cause any immediately-noticeable problem. Instead, you'll get garbage for reads, and will corrupt memory when you write. (Technically, you're invoking Undefined Behavior.) You should really include bounds checking in your accessor methods.
Also, a style note: it's no longer good ObjC form to put ivars in an object's interface. Hide it in the #implementation block as I've done.
An NSInteger is simply a C 'int' (or 64-bit long on 64-bit platforms - the size is immaterial though).
So what you've declared is nothing but a C array, of integers. Since they're simple integers, there's no NSNull.
If you'd declared an NSArray (of NSArray for 2D) then you'd be looking at NSNull and NSNumber in the elements.
After updating Xcode to version 5.1, I had a warning that told me I had defined a constant that I wasn't using. Its definition looked like this:
static NSInteger const ABCMyInteger = 3;
I was happy to see that it got marked, because I thought this meant that the compiler was now able to check for unused constants in addition local to variables.
I refactored some more, making three NSString constants obsolete. All three were defined very similarly to the NSInteger from above:
static NSString *const ABCMyString = #"ABCMyString";
To my surprise, however, these do not get marked as "unused", though I know for sure that they aren't used anymore.
Can someone explain why an NSInteger does get noticed by the compiler as unused, but an NSString does not?
A primitive variable is just a memory block allocated in a static memory part and initialized by the compiler. The string object, however, is a variable initialized at runtime (in startup, probably), so the compiler adds an implicit call to the constructor and uses the variable as a parameter for that call. So the variable is being used.
The _unused item of the structure is IMHO not a directive, but just a member variable, probably it is added for better alignment (fills the object size to a round size).
The definition of an NSString literal at compile time rely on the use of the NSSimpleCString meta class.
This class looks something like this:
#interface NSSimpleCString : NSString {
#package
char *bytes;
int numBytes;
#if __LP64__
int _unused;
#endif
}
#end
#interface NSConstantString : NSSimpleCString
#end
The addition of the _unused flag make me believe that further down the implementation of NSSimpleCString the code will instruct the compiler to silence those warnings with __unused.
You can try yourself by prepending your integer or float constant with __unused like:
__unused static const NSInteger ABCMyInteger = 3;
For a more in depth explanation read the article on literals by Mike Ash
I'm trying to determine if there is an elegant solution to this issue.
Say I have a global defined in some header:
Constants.h:
extern NSString *someGlobal;
And then I wish to use this global in some other class:
Foo.m
NSString *localVariable = someGlobal;
This all works just fine if I initialize the global like this:
Constants.m:
NSString *someGlobal = #"Some String Literal";
But lets say I need to initialize the global to something that isn't a compile-time constant. In such cases I typically do this:
Constants.m:
#implementation Constants
+ (void)initialize {
someGlobal = ... // some non-trivial initialization
}
#end
Now I have a potential problem in Foo.m. If no reference has been made to the Constants class when I try to use someGlobal, the result is nil. A workaround is to do:
Foo.m (or in some app startup code):
[Constants class];
That will trigger the initialize method of the Constants class and someGlobal will be properly initialized. As long as this is done before any runtime use of someGlobal, things work fine.
Is there a better way to initialize extern globals with non-compile time constants without the need to call code such as [Constants class] at app startup?
A more idiomatic way in Objective-C is using a singleton instead of multiple globals. Here is how:
#interface Globals
#property (readwrite,nonatomic) NSString *myString;
#property (readwrite,nonatomic) int myInt;
+(Globals*) instance;
#end
+(Globals*) instance {
static dispatch_once_t once;
static Globals *inst;
dispatch_once(&once, ^{
inst = [[Globals alloc] init];
inst.myString = #"Some String Literal";
inst.myInt = 42;
});
return inst;
}
Now you can use your globals like this:
NSLog(#"Global string: %#", [Globals instance].myString);
NSLog(#"Global string: %d", [Globals instance].myInt);
No, there is no better way. Logically, if some piece of code must execute before a variable is intialized, you have to take steps to make sure that happens.
You could arrange the flow of your program's code so as to guarantee that the Constants class get initialized before any other piece of code executes which needs it. For example, by tweaking the order in which things are initialized in your program and following the order of code execution from main() on down to prove to yourself that it works. But short of that (and the safest thing in any case), you would use your technique to force it to be made valid right before you use it.
Like dasblinkenlight's answer, this may not be exactly what you are looking for but it's another approach.
I would make class methods that returns the value you are looking for like this:
+(NSString *)someConstant {
static NSString *constant;
if(constant == nil)
constant = //your initialization here;
return constant;
}
Then where you need to use it just call [Constants someConstant];
Other random thoughts:
A constant that isn't some compile time value isn't really what extern variables are for and this method insures that the variable is initialized every time you use it. The class using the constant has to know about your class anyway or it wouldn't have imported its header file
I have a PList where I load a couple of rows of data in a dictionary. I want to add the a line like
<key>StandardValue</key>
<string>STANDARDVALUEFORCERTAININSTANCE</string>
Now when I read out the values I get a NSString. How can I get the value of the constant that I previously defined with
#define STANDARDVALUEFORCERTAININSTANCE 123
Is there a way to get the constant representation of a string? So essentially to parse it?
What you want to do isn't exactly possible. The constants created with #define only exist at compile-time, and at run time there is no way to access them by name - they have been converted to the constant value already.
One alternative that might exist is to define a number of methods that return constant values, say in a Constants class. Then, at run time, load the name of the method from the plist and call it using NSSelectorFromString() and performSelector:.
However, a possible issue with this is that for safety with performSelector: you'd have to rewrite all your constants as Objective-C objects (since performSelector: returns type id). That could be quite inconvenient.
Nevertheless, here is an example implementation of the Constants class:
#implementation Constants : NSObject
+ (NSNumber *)someValueForACertainInstance
{
return #123;
}
#end
And example usage:
NSDictionary *infoDotPlist = [[NSBundle mainBundle] infoDictionary];
NSString *selectorName = infoDotPlist[#"StandardValue"];
SEL selector = NSSelectorFromString(selectorName);
NSNumber *result = [Constants performSelector:selector];
And how the selector name would be stored in the info plist:
<key>StandardValue</key>
<string>someValueForACertainInstance</string>
You can't do it this way. I suggest a nice alternative: KVC.
You declare this variable as class instance:
#property (nonatomic,assign) int standardValueForCertainInstance;
Then you get the value with valueForKey:
NSString* key= dict[#"StandardValue"];
int value= [[self valueForKey: key] intValue];
I'm using NSUSerDefaults to store user preferences. I remember reading somewhere that setting the keys as constants is a good idea - and I agree. The following code is what I currently have:
[[NSUserDefaults standardUserDefaults]
setObject:[NSNumber numberWithInt:polygon.numberOfSides]
forKey:#"polygonNumberOfSides"];
I tried changing this to:
#implementation Controller
NSString const *kPolygonNumberOfSides = #"polygonNumberOfSides";
-(void)savePolygonInfo {
[[NSUserDefaults standardUserDefaults]
setObject:[NSNumber numberWithInt:polygon.numberOfSides]
forKey:kPolygonNumberOfSides];
}
While this does work, it produces "warning: passing argument 1 of 'objectForKey:' discards qualifiers from pointer target type". I'm keen to keep my code free from compiler warnings. How can I fix this warning?
You should use:
NSString * const kPolygonNumberOfSides = #"..."; // const pointer
instead of:
NSString const * kPolygonNumberOfSides = #"..."; // pointer to const
The first is a constant pointer to an NSString object, while the second is a pointer to a constant NSString object.
It is a subtle difference. The compiler warning happens because setObject:forKey: is declared as follows:
- (void)setObject:(id)value forKey:(NSString *)defaultName;
It is expecting the defaultName argument to be of type NSString *. When you instead pass in a pointer to a constant, you've given it something different.
Update: I want to point out that these constants should be defined as static if they are only going to be used from within a single file. I say this because I have run across this problem myself: if you do not declare them as static, then they will exist in the global namespace, and you will not be able to use a variable with the same the same name in another file. see Constants in Objective-C for more information. To explain by example, this is what I currently use for keys that I only need to use in one .m file:
static NSString * const kSomeLabel = #"...";
Don't use const with Objective-C objects, they weren't really designed to use it. NSString objects (among many others) are already immutable by default by virtue of their design, so making them const is useless.
As e.James suggested, you can use an NSString * const, which is a constant pointer to an NSString. This is subtly different from a const NSString * (equivalent to NSString const *), which is a pointer to a constant NSString. Using a NSString * const prevents you from reassigning kPoly to point to a new NSString object.
For access from other classes:
.h
extern NSString * const PolygonNumberOfSidesPrefsKey;
.m
NSString * const PolygonNumberOfSidesPrefsKey = #"PolygonNumberOfSides"
For access only inside current class:
.m
static NSString * const kPolygonNumberOfSidesPrefsKey = #"PolygonNumberOfSides"
I would suggest even making the constant more descriptive. A constant for the number of sides of a polygon could come from anywhere. As a suggestion, how about:
kDefaultsPolygonNumberOfSides;
instead.