I know this question has been asked before, but I can't seem to find information about it in Apple's documentation; maybe some of you guys did.
A lot of Objective-C code has cross-file constants in a .h file, using #define.
Others use the approach of a .m with constants and extern them in the .h file.
I understand the difference, both pros and cons, but does Apple state which one to use in iOS development?
The trouble with using #defines over an extern, is that the compiler doesn't get to do any type checking. If you #define a string, there is nothing to stop you using it where you actually want, say, a number. If you use a static NSString instead, the compiler will emit a warning if you try to use it somewhere where it isn't expecting a string.
Apple's recommendation is extern:
Define constants for strings used for such purposes as notification names and dictionary keys. By using string constants, you are ensuring that the compiler verifies the proper value is specified (that is, it performs spell checking).
Admittedly they are inconsistent about this sometimes.
A #define defines a macro which is replaced before compilation starts where as extern *** *const merely modifies a variable so that the compiler will flag an error if you try to change it. There are some cases in that you would use a #define because you can't use a extern *** *const. In theory a extern *** *const will take up memory and requires a reference to memory but this is insignificant as it maybe optimized away from the compiler.
extern *** *consts are a lot more compiler and debug friendlier then #defines this can be the deciding point when you decide which one to use.
Some see that pre-processor directives like #define are frowned upon which would suggest you should be using extern *** *const over #define
But whilst the pre-processor is frowned open some say it is more secure then a variable as it can't be changed at runtime whereas a variable can.
Both have there advantages and disadvantages and I don't think (I can't find anything myself) that Apple recommends one over the other. My personal opinion is to use a mix of them both using a pre-processor directive #define over a extern *** *const where it would seem more beneficial, this is what I do.
If you have some global constants, for example in a Constants.h which is imported in your prefix header and you're using a #define macro for these constants it's going to rebuild your whole project if you make any changes to these constants. In that case it is better to split your constants and use extern for strings, integers and everything else that you can use extern for.
For example if you have extern NSString *const kServerURL; and you change your server address it's not going to rebuild your whole project but if you use define there, it's going to rebuild it. So the only purpose at least for me is for optimising the compile time.
Related
I have an iOS application I'm writing. I've moved away from #define to create my constant values. I have a few questions regarding the use of these style declarations: NSString *const segueToMainMenu
If I'm using these internally, I'm placing them inside the .m file. However, should I be placing these wrapped around the #implementation block or outside of it or does it matter? I'm thinking inside, because they are specific to the implementation and not global, but I'm not sure. Any details are appreciated.
If I'm creating a more global scope using the extern keyword and I'm using a Constants file pair (Constants.h/Constants.m) do I need to place those in the #interface section and then define them in the #implementation section? What is the benefit of that vs the old way of just using a Constants.h file and including it with other headers? Why do I now need two files?
Is the standard practice still to name the constants with a "k" prefix (e.g. kAnimationDuration) or should I now be doing something like MainMenuViewControllerAnimationDuration? I'm imagining yes and if so, does it matter for the constants from number 1 (i.e. not extern) how I name them? In other words, are those visible outside of my implementation?
Clarification is much appreciated.
Doesn't matter whether you place them inside the implementation block or not—only methods are part of the class implementation, so the scope of constants won't change regardless.
The k prefix is a bit dated now. The usual way is to name constants as <prefix><name>, such as "MDSomeConstant".
At the moment I create constants in this way:
// Constants.h
FOUNDATION_EXPORT NSString *const kTestConstant;
// Constants.m
NSString *const kTestConstant = #"TestConstant";
This of course works fine, however I'm puzzled on why I can't just have it all in the header file like this:
NSString *const kTestConstant = #"TestConstant";
If I do that, include Constants.h in various classes and use kTestConstant in those classes, I get redefinition errors at compile time. Why is that?
My theory is that by having a constant only on a header file, the file Constants.h is 'copy-pasted' into the class files that import it, consequently I end up with two copies of kTestConstant. However, by using an implementation file, that file is compiled and linked with the classes that import Constants.h. Is this more or less correct?
Essentially your analysis is correct except for the "modern" concept of "paste". The compilation unit is a concatenation of all header files directly or indirectly included/imported and the implementation file.
As always I recommend obtaining a good "C" language book and studying it, Objective-C is just a thin layer on top of "C". That is what I did years ago, still have the book.
I have a project, with a file that I call 'Keys.h'
In that file, I declare strings and integers that are used across the project, some of which are integers, some of which are strings.
All of the strings work fine; however, if I use integers, I get an unused variable warning.
For a string, (lfPrefs is a dictionary of user preferences)
static NSString * kUserLFPrefs = #"lfPrefs";
This works fine, and does not produce any errors.
For an integer, (I have integers to define the current mode because it seems a bit snappier than comparing strings all the time).
static int kModeLiveFeed = 1001;
static int kModeEventFeed = 2002;
These work just fine, except that they are showing an unused entity warning.
I'd prefer to use the integers over strings, mostly because I read that comparisons are much faster, takes up less memory, etc.
My question is how can I stop the warnings while still getting access to my integer keys?
(Or, should I just use strings)
I can suggest two different methods.
If you want to keep such variables in .h file, you may prefer using define if you will not be changing the value run time like;
#define kModeLiveFeed 1001
If you will be changing the variable value run time, I suggest keeping them in a .m file instead of in a .h file and creating only one instance of the .m file by using singleton. Then, even if you continue to get a warning from the .m file, you can disable it by the steps below:
Select your project from the left navigator to open project settings view.
Then, select your target.
Go to Build Phases tab and open compile resources area.
Click to the right side of your .m file to add a compiler flag as -w
I hope it helps.
You may be misunderstanding the meaning of static in C/Objective-C (this question should help). You should use const rather than static to define constants, and you should define the value of an integer/string constant in a .m file, with a corresponding declaration in the .h file. Or better yet, use an enum if you have a related set of integer constants.
Here is Apple's documentation on constants, which includes the above information as well as naming recommendations (e.g., PRConstant is preferred over the classic Mac OS-style kConstant).
Im my AppDelegate.h
I am just defined the constants:
#define XXDefaultFeedbackRecipent #"feedback#app.com"
#define XXDefaultFeedbackSubject #"Feedback"
What is the right place to define these type of settings? They are not user settings but they do have the possibility of changing from one release to the next.
There's no right place but you may either put it in a 'Constants' file (I usually create a class called constants, delete the interface and implementation of the class and keep the files for this purpose), or in the class where you use those defines.
A better way to keep this data, however, is to use the following:
// in your .h file
extern NSString * const XXDefaultFeedbackRecipent;
// in your .m file
NSString * const XXDefaultFeedbackRecipent = #"feedback#app.com";
p.s. there's a convention about writing #defines that wants you to write the names of your #defines in capital letters with words separated by underscore (e.g. MY_DEFINE). This is to prevent collisions with other stuff in C libraries and other files. Keep this in mind when writing your #defines.
You can keep them as constants in your class, and access them via extern in your .h file.
I would also recommend using consts, for type safety.
In your .h
extern NSString * const XXDefaultFeedbackRecipent;
extern NSString * const XXDefaultFeedbackSubject;
In your .m
NSString * const XXDefaultFeedbackRecipent = #"feedback#app.com";
NSString * const XXDefaultFeedbackSubject = #"Feedback";
I recommend the approach the other answers have explained. Using extern NSString * const
Avoid #defines for this sort of thing as everywhere you reference it, a new NSString will be allocated. Not a problem if your only referencing it once or twice but far from optimal.
Be careful not to mis-use this. For config values, service endpoints, etc. consider using a plist to store values. It makes editing config values much easier and allows for further flexibility with continuous integration setups, multiple service environments and remote config updates by push notification.
I am accessing to a a object stored in NSUserDefaults by using a key string from several places in my project. To avoid a mistake when typing a key string i would like to set in global. Is it possible ??
[[NSUserDefaults standardUserDefaults] objectForKey:#"UD_GPS_LAST_UPDATE"];
There are different ways to do that. Two common methods is to use a global NSString constant or a preprocessor #define directive.
Global constant
A popular approach is to use a global variable. You need to add it to some file. It could be an existing file or a separate file. Make sure that it's outside the #implementation section if it exists. It could look something like this:
NSString *const MyStringConstantIdentifier = #"UD_GPS_LAST_UPDATE";
Then add the same identifier with the extern attribute to a header file which you include in all source files where you want to use the string constant.
extern NSString *const MyStringConstantIdentifier;
Now MyStringConstantIdentifier will refer to the same string in all places where it's used.
[[NSUserDefaults standardUserDefaults] objectForKey:MyStringConstantIdentifier];
Preprocessor directive
Another approach is to use a preprocessor #define directive in a header file. Make sure that you include the header file in all source files where you want to use the identifier.
#define MyStringConstantIdentifier #"UD_GPS_LAST_UPDATE"
Now when you include that header file MyStringConstantIdentifier will be available as a shortcut for writing #"UD_GPS_LAST_UPDATE". This will however put the burden on the preprocessor rahter than the compiler. The difference from using a global variable is that when you use
[[NSUserDefaults standardUserDefaults] objectForKey:MyStringConstantIdentifier];
the preprocessor will actually substitute MyStringConstantIdentifier with #"UD_GPS_LAST_UPDATE" so that the code that the compiler processes looks like this:
[[NSUserDefaults standardUserDefaults] objectForKey:#"UD_GPS_LAST_UPDATE"];
Where this can be a problem is if parts of your code ever moves into a library. Because preprocessing happens at (actually just before depending on how you look at it) compile time the substitution will replace the constant with the string at all places where it is used. Let's say that this is defined in a library. Whenever the string is changed in the library any application which uses it will have to be recompiled.
Yes it is possible.
Indeed this is the best way to use all the constant strings.
You can create a GlobalConstantsAndKeys.h file with below and others
#define kUDGpsLastUpdate #"UD_GPS_LAST_UPDATE"
and then use it throughout your project.
[[NSUserDefaults standardUserDefaults] objectForKey:kUDGpsLastUpdate];
Yes, create example.h file and write #define SEND_MESSAGE_ID #"2" or what ever u want then go to .pch file and import example.h to see it in all app.