if you don't take #define into account, there are two notorious ways for declaring constants in objective-c project:
// Util.h
extern NSString * const MyConstant;
// Util.m
NSString * const MyConstant = #"value";
or the other directly in header file
// Util.h
static NSString *const MyConstant = #"value";
Now, there comes two questions:
1)
Both works, the second method is quite convenient, as I have only one place for editing values.
However as I saw from Apple .h files, the first method is always preferred, and I wonder if there are any downside, with the static method.
2) Looking at Apple docs, we often encounter very long constant name like: NSTextInputContextKeyboardSelectionDidChangeNotification. In the case you used a long constant name like that, what convention would you generally use to assign a value. If I want to use something descriptive I could use #"nsTextInputContextKeyboardSelectionDidChangeNotification", but sounds little odd.
I won't explain you all the specifics of the two types of constant declaration - in short:
The first one separates constant declaration from constant definition. Only declaration is included by header files. It also conveniently hides the actual value of the constant.
The second one is more problematic - the header file contains both declaration and definition of the constant. That means that whenewer you include the header, the constant is created again. I don't think this will work correctly when included from multiple files.
The second question - no problem with long constant names. Your example is a bit extreme but there is nothing wrong with it.
EDIT: Adding more information about the static NSString* const in header.
Let's have a header A.h:
//A.h
static NSString *const MyConstant = #"value";
and file including it A.m
//A.m
#import "A.h"
printA() {
NSLog(#"A.h Constant: %#", MyConstant);
}
First note that headers are removed by the preprocessor before compilation. That means that before compilation there won't be any A.h file and A.m will look like this:
//A.m
static NSString *const MyConstant = #"value";
printA() {
NSLog(#"A.h Constant: %#", MyConstant);
}
Let's create another constant header B.h and implementation file B.m with exactly the same contents:
//B.h
static NSString *const MyConstant = #"value2";
//B.m
#import "B.h"
printB() {
NSLog(#"B.h Constant: %#", MyConstant);
}
Note that the constant is declared twice, with the same name and different values. This is possible because static makes the constant private for the file that includes it. If you remove the static, you'll get a compilation error because the compiler will find two public global constants with the same name.
In theory it is possible to use static NSString* const in header files and everything will work correctly but as you can see, it doesn't do exactly what you want and can be a source of difficult-to-find bugs. That's why you should use static only from implementation files.
Just to add - the second way that you have shown doesn't make much sense.
If you are going to declare and define a constant in one place such as this, it's usually done in the .m file and not the .h file.
You do it this way for constants that are only going to be used in the class itself and not exposed.
Related
I am taking a Constants.h file for saving all my values. And i declare like this :
static NSString *NAME = #"Name";
static NSString *MAILID = #"Mail-id";
static NSString *ADDRESS = #"Address";
I also use this key in my code. But always its showing a warning Unused variable "NAME"
If i take
const NSString *NAME = #"Name";
Then its showing Error. So please anybody can help me, how can i resolve this warning ?
Thanks :)
In C, and therefore in Objective-C, a static is a variable with file-level scope. Both #import and #include have the same effect as copying and pasting the header file's text into your source file. So with static, each file that includes the header is getting its own personal copy of the symbols NAME, MAILID, etc. If you don't use one the compiler will therefore warn you.
const means constant. If used before the asterisk then it means a constant NSString, which doesn't have a lot of semantic sense because NSString is an opaque type. If you just use const you're also having every file that includes the header redeclare the variables. So you'll get a liker error.
What you probably want in the header is:
extern NSString *const NAME;
That says: (i) estern — not here, but in another unit; and (ii) NSString *const — it is a pointer to NSString and the address within the pointer never changes.
Then in any single implementation file add:
NSString *const NAME = #"NAME";
As otherwise every single compilation unit will sit back and expect one of the others to define it. You'll therefore get a linker error that the symbol is undefined.
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
In my project I am working on it since last 6 months , i was using #define to define all URL in a FileManager class.but Now I came to know that I have to use two different URL depend on condition so dynamically I can not change #define values.so I decided to Use extern variables in FileManager class instead of #define.
as
in interface file
extern NSString *Url;
extern NSString *loginUrl;
in m file
#implementation UrlManager
+ (void) initialize{
Url=#"http://xxxxxxxxxx.com";
loginUrl=[NSString stringWithFormat:#"%#%#",Url,#"/ipad.php?method=audLogin&username="];
}
but when I am importing URLManager.h , and using loginUrl, still its null , please help me how can I initialize extern variables so that I can use these in app.
First, if you have a URL, use NSURL to reference it.
Secondly, name your globals consistently and descriptively. AliRootURL and AliLoginURL would be descriptive and has a prefix (Ali -- change it) to make it easy to recognize.
Finally, don't use globals at all. Instead, have class methods on your URLManager class (which really should be prefixed, too) that return the URLs:
#interface AliURLManager:NSObject
+(NSURL*)rootURL; // no need to prefix methods in your custom classes
+(NSURL*)loginURL;
#end
That way, you can implement those methods to initially return URLs directly:
+(NSURL*)rootURL
{
// use a static and dispatch_once() if you want only one instance
return [NSURL URLWithString:#"http://foobar.com/"];
}
+(NSURL*)loginURL
{
NSURL* root = [self rootURL];
NSURL* loginURL;
... calculate loginURL here... (http://stackoverflow.com/questions/718429/creating-url-query-parameters-from-nsdictionary-objects-in-objectivec)
return loginURL;
}
That way, you could easily refactor your code to, say, read URLs from the defaults database or have a different configuration for development.
Bummer -- but I can understand the desire to not change lots of files. Should be search and replaceable, though (at least, would be if your variables were a bit more descriptive).
If you are going to initialize the globals via +initialize, you need to ensure that the class is tickled. Add [URLManager class]; to your applicationDidFinishLaunching:
I'm trying to make a key for use in objc_setAssociatedObject, as in this question:
How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?
I have a MyConstants.h file which defines
static NSString *myConstant = #"MyConstant";
This file is then included in MyFramework via MyFramework.h
#import ...;
#import "MyConstants.h"
#import ...;
Then in my project, the framework is included in all files via the .pch header.
#import <Cocoa/Cocoa.h>
#import <MyFramework/MyFramework.h>
This all works as expected. The problem is that when I call objc_setAssociatedObject or objc_getAssociatedObject with myConstant as the key, it doesn't work. The reason is clear enough from the log:
- (void)doSomething:(id)key { //Gets called with myConstant
NSLog(#"Associating with key at %p", &key);
objc_setAssociatedObject(self, &key, object, policy);
}
- (void)doSomethingElse:(id)key { //Gets called with myConstant
NSLog(#"Seeking association for key at %p", &key);
NSLog(#"Will find %#", objc_getAssociatedObject(self, &key));
}
Which logs:
Associating with key at 0x7fff5fbfecdf
Seeking association for key at 0x7fff5fbfecef
Will find (null)
You'll notice that the pointers shift.
However, calling the same method again shows the same pointers. It's as if the constant IS a constant, in the file that imports it, but not for the whole project.
Q: How can I correctly define and import headers with constants so that a constant is actually at a constant place in memory?
If two files have static char something, each one will have its own copy--they do not represent the same piece of memory. If you want two files to have access to the key, you must instead do this in your header:
extern const char MyConstantKey;
And this in one implementation file (.c or .m):
const char MyConstantKey;
This defines a single one-char-wide location in memory, to which all files will point when you use &MyConstantKey.
I have edited the original post to show what I think the error was. The other poster's answer may well have also been an excellent solution, but how I resolved the problem was like so:
As the &key indicates, I was passing key directly to the methods, as in myMethod:(id)key. Somehow, de-referencing this pointer was different in different cases. By simply changing all the methods to myMethod:(void *)key and passing in &key, the problem immediately went away. Or ensuring I was using a static char and not a static NSString *
I don't understand it fully, but it works.
I have a large constant (an NSString with 10^6 values). Because of its size I would like to declare it at the end of the source file (so I don't have to scroll through it every time I want to edit my code). Also because of its size I would like it to be a constant so I can load it at compile time instead of runtime. Also, because I do not want it accessible to outside users I do not want to declare it as extern in the header file.
I have it declared as a constant using the code below in the implementation file, however it is giving me a "Use of undeclared identifier 'hugeConstantString'" if I move it past the #end of the implementation (for obvious reasons).
NSString *const hugeConstantString = #"a_whooooooole_lotta_characters";
I've checked this out: Constants in Objective-C but it didn't tell me anything I didn't know already. Maybe my brain is fried, but: is there any way that I can define this huge constant AFTER my implementation and still have it accessible? Or if I declare it in another header file and import it, will it then be accessible to others?
Thanks!
I'm not sure such a large string is a good idea, but if you're going to use it, I suggest putting it in its own header file.
MyLongStringConstant.h
#define kLongString #"..."
MyClass.h
....
#import "MyLongStringConstant.h"
...
//Do something with kLongString
...
If you want to have it accessible in every file of your app, import the header inside your apps myApp_Prefix.pch file, which is imported into every file.
I am going to save the conversation of Why are you doing that and just post a simple solution for you. Thanks to Tommy in the comments here is a simpler version.
#import "LargeStringTest.h"
#implementation LargeStringTest
//Declare the string
static NSString *hugeConstantString;
- (id)init {
self = [super init];
if (self) {
NSLog(#"Large String %#", hugeConstantString);
}
return self;
}
//Place all other code here
//Assign the string
static NSString *hugeConstantString = #"a_whooooooole_lotta_characters";
#end