7

I understand that placing the word extern before a variable declaration in a header file declares the existence of a global static variable without initialising it. I also understand that if I import the file containing the extern variables, I can reference them without a class/file name. But where does one define them and their values?

What I am trying to do is create a class of constants with global constants that I want to use throughout an iOS application's code.

Does one put them inside the interface like this?

Example.h

#import <Foundation/Foundation.h>
@interface Constraints : NSObject
{

extern   NSString * const PREFS_NAME;

}

Or does one put then outside of the interface like this

Example.h

#import <Foundation/Foundation.h>

extern   NSString * const PREFS_NAME;    

@interface Constraints : NSObject
{

}

Then in the implementation .m file how would one initialise the extern values?

Inside the implementation area like this?

Example.m

#import "Constraints.h"

@implementation Constraints

/**PRefecences name for the application**/
const  NSString * PREFS_NAME = @"MyApp_Prefs";

@end

Or do initialise them outside of the implementation area like this:

Example.m

#import "Constraints.h"

/**PRefecences name for the application**/
const  NSString * PREFS_NAME = @"MyApp_Prefs";

@implementation Constraints

@end

Or do I provide them their initial values in a constructor? or some arbitrary a static style method with + in front of it i.e. +(void) setAppConstraints;

I have tried several combinations, but always run into errors, such as "Redefinition of 'xVariable' with a different type". Or a something about "extern does not have an initialise interface" (or something like that, I forget). So I want to know how to declaire and initialise them properly to form the same role as public static final variables in Java.

Also what are the limits of the extern command? I know I can extern an NSInteger or NSString, but what about NSArray?

I am asking this question because there seems to be to much misleading, or incomplete, information regarding the use of extern in Objective-C. Many of the answers seem speculatory. My hope is for this question to be a good resource not only for me, but to limit further similar questions about the basics of extern.

jscs
  • 63,694
  • 13
  • 151
  • 195
Andrew S
  • 2,847
  • 3
  • 33
  • 50
  • possible duplicate of [Use of 'extern' keyword while defining the variable](http://stackoverflow.com/questions/7606909/use-of-extern-keyword-while-defining-the-variable) – jscs Feb 16 '13 at 19:07
  • If you want to "create a class of constants", that has nothing to do with global variables, because they don't belong to a class. You need ivars for that. – jscs Feb 16 '13 at 19:09
  • @Josh Cashwell possible its a similar question. But as I stated in my question text, all of the answers I found on S/O were incomplete or left more questions than they did provide answers. – Andrew S Feb 16 '13 at 19:17
  • @Josh Caswell I want to create a globally accessible constant values. That does not suggest iVars to me. I suppose I could use a singleton but that still does not explain how this could be acheived with extern, which correct me if I'm wrong is a valid use case for the command and one which it was designed for. – Andrew S Feb 16 '13 at 19:20
  • Your question has several misconceptions in it, so I find it hard to believe that you've _really_ read all the duplicates you say you've found. As for "globally accessible constant values", if that's what you want, then you don't want a class. – jscs Feb 16 '13 at 19:21
  • @Josh Caswell Bingo, I want to use extern so how? – Andrew S Feb 16 '13 at 19:23
  • Can you then please explain what it is, and why it is always mooted as a viable alternative Java's public static class variables. – Andrew S Feb 16 '13 at 19:34
  • Have you seen these three questions? [Referencing a static NSString const from another class](http://stackoverflow.com/q/11942458), [Where various variable and method types should be placed in a header?](http://stackoverflow.com/q/11007057), [On lazy instantiation and convenience methods](http://stackoverflow.com/q/8497966) – jscs Feb 16 '13 at 19:43
  • @Josh Caswell Yes, these were exactly the kind of articles I was referring too. None of these explain where in the .m file I should initialise my exten variables. All they say is in the .m file. They also do not answer my question on what data types I can use with extern for example are arrays allowed? – Andrew S Feb 16 '13 at 23:39

2 Answers2

13

You define it's value in the file inside which it's declared, which in your case is Example.m; You can still re-assign this variable, so the declaration in Example.h would look like this:

extern  NSString * PREFS_NAME;

This way every file that imports Example.h has access to this variable. The equivalent of public static final in Objective-C is const. If you also want it to be public you should make it be a class instance variable, but in this case you don't need it because it's already accessible everywhere. So in this case it would be:

// .m file
NSString* const PREFS_NAME = @"MyApp_Prefs";
// .h file
extern NSString* const PREFS_NAME;

Also notice that const NSString* is different from NSString* const. The latter is a const pointer to NSString. The former hasn't sense even if it's a correct syntax. In Objective-C the const qualifier doesn't affect objects, instead there are mutable and immutable classes. It would have sense in C++ meaning that you can use just const methods on the instance.

Ramy Al Zuhouri
  • 21,580
  • 26
  • 105
  • 187
3

extern is used to signal the compiler that you will be using a variable or a function that is defined in another compilation unit.

When you say extern const NSString *PREFS_NAME, you're saying "Replace all references in this compilation unit to PREFS_NAME to the variable PREFS_NAME as it is defined in another file." So when you try to assign PREFS_NAME in your .m, all you're doing is trying to assign a variable that, though it has a name, it doesn't exist. Declaring a variable extern is only a declaration of a variable or function, not a definition of that variable or function. It lets the compiler know that the name is in use, and that the linker will take care of what to do with it, but even if you provide a type here, it doesn't actually set aside space for the variable, it's expecting the space to be set aside in the compilation unit that's actually defining the variable.

You compile three or four different source code files together, three of them may declare:

extern int buffer[];

And one may declare

int buffer[BUFSIZE];

In its global scope, and the linker's job is to resolve the three declared references to extern buffer to the fourth's actual definition of the buffer.

extern is to C variables and functions much as @class is to Objective-C classes, it's a forward declaration, a promise to the compiler that you don't have to freak out when you see a name that's undefined here, because the linker will answer whatever lingering questions you may have.

iluvcapra
  • 9,436
  • 2
  • 30
  • 32
  • That's as I understand it so far. So how do I initialise an extern variable so the whole project can see it and its value? – Andrew S Feb 16 '13 at 19:27
  • You can init it in the .h file, the very first time you declare the variable `extern`, or you assign them in the global scope of your .m file. You would NOT use the `extern` keyword when you do the initializing, and you would NOT use the `static` keyword. `static` is the opposite of `extern`, it restricts the variable's definition to the compilation unit and doesn't allow others to refer to it. – iluvcapra Feb 16 '13 at 19:35
  • PS Most people doing this in Obj-C would just do `#define PREFS_NAME @"MyApp_Prefs"` ... We all run shrieking from the thought of `public static final` and what it might mean :) – iluvcapra Feb 16 '13 at 19:37
  • So as in my original post, is that inside or outside the @ implementation area of my .m file. Please give an example, I would be very greatfull. There seems to be a lack of concrete examples hence my confusion. – Andrew S Feb 16 '13 at 19:38
  • 2
    I don't have my Xcode with me today so I can't guarantee, but your declaration in .h should be outside of your `@interface` and looks basically right. Your line in .m should be outside of your implementation and just read: `NSString *PREFS_NAME=@"MyApp_Prefs";` No further qualification should be required, also make sure you're `consts` are in the same place both times. `const NSString *` and `NSString * const` mean different things. – iluvcapra Feb 16 '13 at 19:50
  • 1
    Regarding [your first comment](http://stackoverflow.com/questions/14913894/equivalent-of-public-static-final-variables#comment20922988_14914121): You should _not_ initialize at the declaration. This is a compiler warning for a good reason: if you initialize the variable in the header, you'll get a linker error everywhere that header is imported. – jscs Feb 16 '13 at 19:53