As subsystems grow, it becomes increasingly reasonable to define many of your constant primitives (strings, ints, floats) in a series of shared files so that they can be reused, both for text-completion in your IDE and for better readability. But there are better and worse ways of doing code reuse, and in Objective-C I find that this is often glossed over.
I know I sure glossed over some of the relevant details in my classes on Ansi C89, and it was such a subtle point that my teachers never spent much time on it either:
What is the difference between each of the following?
int i1; int *i2; int const i3; int const *i4; int *const i5; int const * const i6;
If you’re a basic C programmer, you certainly know that i1 is an integer and i2 is a pointer to an integer.
You probably also know that i3 is an immutable integer (meaning read-only) and i4 is a pointer to an immutable integer. And if you knew that, you might quickly remember that in C convention i2 (or i4) could actually be an array of integers (or immutable array of integers).
This is very common in C, with strings:
const char *c = "Hello world!\n"; const char c = "How you doing?\n";
As a student you learn to write
const char almost without thinking about it. For generations–since time immemorial–the C compiler has been putting all of the compile-time strings into a read-only table as an optimization, thus the practice of writing something like
const char * becomes second nature. (In fact, back in my day C89 didn’t even know what to do with a non-const char *. The results were undefined if you tried! C11 might be smarter now, but I doubt it.)
char const * const? Sure looks a bit weirder!
This is something they kind of glossed over in my classes so honestly I never gave it much thought, but understanding this is important if you intend to define a file full of “constants” that are used throughout your application.
int *const i5;, I create a read-only pointer to read-write memory. Which means the pointer will always point to the same location in memory, but the value at that location can change.
int const * const i6;, I create a read-only pointer to read-only memory. Once I set the data at i6, nobody going to change it out from under me for the rest of the application’s lifetime. Programmers who make sure their memory is always defined as properly mutable or immutable are aiming for something called const-correctness. Const-correctness is a good thing, even if it can be rather subtle.
Okay, so how does this relate to Objective-C? Well…
Have you ever written a constants file full of strings that looked like this?
static NSString *optionA = @"Option A"; static NSString *optionB = @"Option B"; // ... 1000 lines later ...
My team has. If so, then you’re just waiting for an unwise programmer to come along and mess your stuff up!
optionA = @"Option B"; // Why not? Nothing stopping me from doing it!
You don’t want somebody to come in and blow away all your constants, do you? Yeah, me neither. You expect your constants stay, well… constant. Objective-C developers are very comfortable with the notion of using immutable NSStrings, but seem to forget that somebody can overwrite their lovely data when their pointers themselves are mutable.
Don’t open yourself up to this pitfall. Start defining your NSStrings with the *const modifier! Get Const-correct.
static NSString *const optionA = @"Option A"; static NSString *const optionB = @"Option B";
You’ll be glad you did.
Oh, but I strongly do not recommend this:
static NSString const * const string = @"DataType is 'const NSString *'";
If you try that, you’ll see that Xcode starts freaking out when you use your string, warning you that you might pass a read-only NSString to a method that isn’t expecting it. Of course, you know better. NSString is already immutable! But the compiler doesn’t know that and it wants you to cast your
const NSString * to an
NSString * every time you use it.
Don’t cast it, fix it. The compiler is warning you because you have a const-correctness violation!
Ok, that’s all. Happy coding.