Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

static const Vs extern const

Tags:

I have been using static const in my header files as so:

static NSString * const myString = @"foo"; 

But have read that this is not the 'safe' or correct way of doing this. Apparently, if I want my const strings to be accessed from another class, I should be declaring the string in my .h as:

extern NSString * const myString; 

Then in my .m file:

NSString * const myString = @"foo"; 

Is this correct? If so, what is the reason not to declare it as static directly in my .h file? It works perfectly fine, and I can not see any 'safety' issues around this. It is a const, so therefore it can not be changed from outside and its something I intentionally need accessed outside of the class. The only other thing I can think of is that to hide the value of the string?

like image 407
ssh88 Avatar asked May 14 '14 10:05

ssh88


People also ask

What is the difference between const and static const?

The const variable is basically used for declaring a constant value that cannot be modified. A static keyword is been used to declare a variable or a method as static.

What is a static const?

“static const” is basically a combination of static(a storage specifier) and const(a type qualifier). The static determines the lifetime and visibility/accessibility of the variable.

Can we use static and extern together?

3.1. Thus, prefixes “ extern ” and “ static ” cannot be used in the same declaration. They maintain their value throughout the execution of the program independently of the scope in which they are defined.

Should I use const or static?

const is a constant value, and cannot be changed. It is compiled into the assembly. static means that it is a value not related to an instance, and it can be changed at run-time (since it isn't readonly ). So if the values are never changed, use consts.


2 Answers

Your first variant

static NSString * const myString = @"foo"; // In .h file, included by multiple .m files 

defines an myString variable locally in each "translation unit" (roughly speaking: in each .m source file) that includes the header file. All string objects have the same contents "foo", but it may be different objects so that the value of myString (the pointer to the string object) may be different in each unit.

Your second variant

extern NSString * const myString; // In .h file, included by multiple .m files NSString * const myString = @"foo"; // In one .m file only 

defines a single variable myString which is visible "globally".

Example: In one class you send a notification with myString as user object. In another class, this notification is received and the user object compared to myString.

In your first variant, the comparison must be done with isEqualToString: because the sending and the receiving class may have different pointers (both pointing to a NSString object with the contents "foo"). Therefore comparing with == may fail.

In your second variant, there is only one myString variable, so you can compare with ==.

So the second variant is safer in the sense that the "shared string" is the same object in each translation unit.

like image 112
Martin R Avatar answered Oct 21 '22 09:10

Martin R


There is no reason that I know of, for declaring anything as external in Objective-C, while you use Objective-C only in your project. I could think of reasons when you mix it with C or assembler modules etc.

However, extern has the advantage that the constant will really exist only ones in the whole project, if it is that what you want to achieve, if you really need to save on these 20 or so bytes. But that carries the risk of conflicting names. Other libraries may have declared their own externals using the same name. And the linker would care for them using the very same space in memory, although they may be of different types.

And yes, the extern declaration in the header should be accompanied by a corresponding definition in the .m file. I am not sure but I think you could assign @"foo" in the .h file already. You could even declare it outside of @interface/@implementation-@end blocks. (Never tried that myself). In that case the variable would be global and accessible from everywhere even without the extern keyword. On compile time the compiler would complain about accessing them when he does not see its declaration within the chain of #include statements. But academically, one single .m file could contain two or more classes (which I clearly do not advise) and then the variable would be accessible from both classes although it belongs to none of them. In the end, OjbC is just an add-on to ANSI C.

However, there is no point in making them static. These constants are static anyway. They are constants. The purpose of a static variable within a class or even method is it's scope (visibility) is limited to that class but there is only one instance on runtime that is shared by all instances of the class.

Example:

@implementation AClass : NSObject   static NSString *someString   - (void) setString:(NSString*) aString{   someString = aString; }  - (NSString*) getString (){   return someString; } 

... and somewhere else:

AClass * a = [[AClass alloc] init]; AClass * b = [[AClass alloc] init];   [a setString:@"Me"]; [b setString;@"You"]; NSLog (@"String of a: ", [a getString]); 

would print out You but not Me

If that is what you want, and only then, use static.

Using simple preprocessor macros (which I prefer, but I am kinda oldschool here) has the disadvantage that these strings would be copied into the binary each time when the macro is used. Apparently that is not an option for you anyway because you did not even asked for them. However, for most usages preprocessor macros in commonly shared .h files would do the trick of managing constants across classes.

like image 24
Hermann Klecker Avatar answered Oct 21 '22 11:10

Hermann Klecker