In Objective-C, are there any specific rules for declaring global variables in .m files? - objective-c

By reading this post, It looks like certain rules should be considered when declaring global variables.
So I tried declaring global variables in the .m file. However, the code sense seems not happy to do this for me. For example, say I already have 2 lines in the .m file:
#implementation ViewController
#synthesize abc, xyz;
Now I want to add "BOOL isChecked;".
If I type this below "#synthesize" (or just between #implementation and #synthesize), the code sense actually suggests me to input "bool" (lower case) as I am typing "BOOL". If I type "BOOL" above "#implementation", it would suggest "BOOL" successfully.
Surely, the global variable is part of this class which means it should be inside the implementation. I am not sure why it doesn't like to let us do this.
This makes me feel that Objective-C doesn't like us to declare global variables below #synthesize. But my question is WHY? What I feel is that there may be a reason or Apple made a bug here.

Surely, the global variable is part of this class which means it should be inside the implementation. I am not sure why it doesn't like to let us do this.
Global variables aren't part of a class. Sure, you can put them inside an #implementation block, but they're really not a part of the class -- they're global -- so they don't really belong there.
Objective-C doesn't have class variables like Java or other languages do. You can fake them with global variables and class methods that access those variables, but at the end of the day, they're global, not specific to a class.

What the IDE's code sense help suggests is not an absolute indication of what the Objective C language "likes". Putting a global variable either outside or inside a class implementation generates equivalent compiled results from the Xcode Objective C compiler, and with no warnings.
However it might be considered better code style to put these declarations outside of the class implementation, as all global variables actually have global scope, not class scope.
In addition, you might want to assign an initial value to your global variables, instead of making any assumptions in your code of what that might be.

Objective-C does not have class variables. You declare a C global variable. This is how I do it:
Declare a static C variable. In your case:
static BOOL isChecked = NO;
Notice that I have initialized it. Notice that it is declared static, which restricts its visibility to the .m file it is declared in.
If you want a global object such as an array, you will need to initialize it:
static NSArray *myArray;
#implementation MyClass
+ (void)initialize {
if (self == [MyClass class]) {
myArray = [NSArray arrayWithObjects: ... ];
}
}
#end
Notice the if (self == [MyClass class]) check. +initialize will be called one or more times. Once when MyClass is used for the first time (e.g. call to +alloc). Possibly again when any subclass of MyClass is used for the first time. Hence the check to see if you are actually initializing MyClass, or a subclass of it.
And finally, if you want to access this variable outside of the code that it is declared in, create an accessor:
+ (BOOL)isChecked {
return isChecked;
}
Don't expose the global C variable by removing the static modifier. It makes it harder to debug, refactor or re-implement (what if isChecked suddenly depends on other state, how do you change this if it is directly used all over the place in other code?)

Related

Where various variable and method types should be placed in a header

I've noticed that I get compilation errors if I place certain declarations in certain places in my header file. I've put comments into the code as to where I think certain things go; are they correct?
#interface Level : CCNode {
//Instance variables?
PlayBackgroundLayer* playBGLayer;
PlayUILayer* playUILayer;
PlayElementLayer* playElementLayer;
}
//Static methods?
+(void) InitLevel: (int) levelNumber;
+(Level*) GetCurrentLevel;
//Property declarations for instance variables?
#property (nonatomic, retain) PlayBackgroundLayer* playBGLayer;
#end
//Static variables?
Level* currentLevel;
PlayTilemapLayer* playTilemapLayer;
You are correct on all counts except one. Your last variables are not static variables, they are global variables. Static variables are simply variables that are declared with the static keyword and they mean something a little different than other languages. They aren't class variables, they are variables that are visible only to the file that they are declared in, and only then in the scope that it was declared (if you declare it inside a function other functions won't see it). However, as you would expect, they are only declared once regardless of how many instances you have. If you declare something outside an interface without the static keyword as you did, other classes will import them. However, this is not the ideal way to accomplish this (you might get redefinition errors if more than one class imports this header).
Also, one caveat, is that properties don't need to have an explicit backing variable (the compiler will create one for you if you use the #synthesize keyword), but of course if you desire one there is nothing wrong with it.
Finally, you should note that the only reason that your static methods class methods are not instance methods is because they start with a plus (+) character as opposed to a minus (-) character.
Instance variables usually don't need to be declared explicitly. They're created when you #synthesize the property. If you do want them, though, the (new) correct place* is at the top of the implementation block:
#implementation Level
{
PlayBackgroundLayer* playBGLayer;
PlayUILayer* playUILayer;
PlayElementLayer* playElementLayer;
}
Those aren't static methods, they're class methods, but, yes, that's where you declare them. Some people like to put #property declarations before the class methods, but that's a matter of opinion. Instance methods go after both of these, although technically speaking the order doesn't matter -- that is, the compiler doesn't care, it's just a matter of readability.
Those top-level variables need to go somewhere other than a header file, though. If you put them there you will get compilation errors because every file that imports the header will appear to be re-declaring storage for those variables, which isn't allowed.
Ususally you put such variables into a .m file. If you want them to only be visible from there, you would use static. If you want them visible from other files that import the header, you leave static off and declare the variable as extern in the header:
extern Level* currentLevel;
This lets the compiler know that the storage for the variable is reserved elsewhere.
*See "Class Interface" in TOCPL.

Does the name of an argument matter when defining a function?

Does it matter if I define a function with one argument name in the .h file, for example...
-(foo *) initWithId:(NSString *)id;
And then in my implementation give the argument a different name because it hides a class property:
-(foo *) initWithID:(NSString *)idString;
I know that the autocomplete files use .h as the 'basis' for their autocomplete fillers, and while it doesn't apply to this scenario, I prefer to use the property name in my functions to remain as consistent in my coding style as possible. It makes more sense to understand that getFoo and setFoo both apply to the same property 'foo' as in -(bar *) initWithFoo:(id) foo;.
As far as I can tell, the compiler doesn't have any issues with it, but somehow it seems like it SHOULD matter.
The LLVM analyzer in Xcode does seem to care about some things like methods starting with new and copy.
Here's a sample warning when I name a property starting with new:
"Property's synthesized getter follows Cocoa naming convention for
returning 'owned' objects"
(the #property had a #synthesize that created a getter method starting with new).
No, the compiler doesn't care. Other people who read your code might care.
the only time it really matters is if you have an instance variable name with the same name.
#synthesize something;
- (void)methodForSomething:(id)something
{
something = something;
}
this will throw an error. obviously the solution is to modify your instance variables naming.
#synthesize something = _something;
other then that, parameter names dont matter.

Declaring variable above vs below #interface

int helloness;
#interface test : NSObject
#end
vs
#interface test : NSObject{
int helloness;
}
#end
Do I understand that following are true and the only meaningful differences between the above two blocks:
in both blocks, the implementation of test.m can use helloness variable internally, like an ivar
in the first block, helloness will exist for any class that imports this .h but is otherwise private only to test.m in the second block
In the first block, is this technically what is considered a "global variable" in that any class that imports this will have access to the same contents of helloness?
What happens if multiple header files have a declaration for helloness and you import them all?
Similar to this, consider this implementation:
#implementation AClass
int food=5;
Here, food acts like an internal iVar, even though it was not declared in any #interface ?
In your first example, helloness is a global variable. It can be seen by any file which imports that header. If you include multiple headers which also declare an int helloness variable, I believe you'll get a warning from the compiler, and all of them will point at the same memory location. If you include another header which declares a helloness of type other than int, I believe you'll get a compiler error.
In the second example, helloness is an instance variable (ivar). Its value (memory location) is specific to each instance of AClass. (Anything can access it: e.g. AClass *instance = [[AClass alloc] init]; instance->helloness = 7; However, direct access to ivars is generally avoided in ObjC -- we use accessors and/or properties instead.)
In the third case, food is still a global variable, but its visibility is restricted to the implementation file it's declared in. Any instance of AClass, as well as any other classes or categories or functions implemented in the same file, can reference food, and all those references are to the same memory location.
In your first example, helloness is a global variable. In your second example, it's an instance variable.
There can be only one global variable with a given name in your program. There is a copy of an instance variable for each instance of your class that's created during your program's execution. They're not semantically similar at all.
Having a global variable in a header file, as I presume you are doing in the first example since you refer to #importing it, is probably a bad idea. If it's not a tentative definition like yours is (for example if you instead had int helloness = 12;), you'll end up with multiply defined symbol errors at link time.
In your last example, food is still a global variable, but since it's likely to be in an implementation file (rather than a header), you probably won't run into any multiply defined symbol errors. It won't work like an instance variable, though - it's still a global variable.

How to use global variables in Objective-C?

How should I declare a global variable in my Objective-C project?
Traditionally, global variables are declared in a header, and defined in a source file. Other source files only need to know how it is declared to use it (i.e. its type and its name). As long as the variable is defined somewhere in a source file, the linker will be able to find it and appropriately link all the references in other source files to the definition.
Somewhere in your header, you would declare a global variable like this:
extern int GlobalInt;
The extern part tells the compiler that this is just a declaration that an object of type int identified by GlobalInt exists. It may be defined later or it may not (it is not the compiler's responsibility to ensure it exists, that is the linker's job). It is similar to a function prototype in this regard.
In one of your source files, you define the GlobalInt integer:
int GlobalInt = 4;
Now, each file that includes the header will have access to GlobalInt, because the header says it exists, so the compiler is happy, and the linker will see it in one of your source files, so it too will be happy. Just don't define it twice!
However
You should consider whether or not this approach is useful. Global variables get messy for a number of reasons (trying to find out exactly where it is defined or declared, threading issues), there is usually not a need for global variables. You should perhaps consider using a singleton approach.
Don't. Global variables are often a sign of poor design. A common replacement in Objective-C is a class method that returns an object (that may or may not be a singleton), such as [NSUserDefaults standardUserDefaults] or [UIDevice currentDevice].
However, if you must use a global variable, read on.
In your header:
extern NSString *someString;
extern NSInteger someInteger;
In your implementation file:
NSString *someString = #"DEFAULT_VALUE";
NSInteger someInteger = DEFAULT_VALUE;
In my experience there are few instances when a program doesn't need, at least, some sort of data or utility/helper methods that can be accessed throughout the program.
They way I deal with this, rather than using global variables is to create what I call a 'project applicance', which is essentially just a class with a bunch of static methods.
It could be implemented multiple ways, but I use a singleton and just have the static methods call through to the single instance of the appliance class. For example, in my project Oovium I have:
Oovium.h:
#interface Oovium : NSObject {
UIWindow* _window;
}
+ (UIWindow*) window;
Oovium.m:
#implementation Oovium
static Oovium* oovium;
- (UIWindow*) window {return _window;}
+ (void) initialize {
oovium = [[Oovium alloc] init];
}
+ (UIWindow*) window {return [oovium window];}
I then include Oovium.h in my Oovium_Prefix.pch file so that it is automatically included in all of my files.
Globals rock! I don't know what everyone is scared of. I used them successfully here.
Passing Data between View Controllers
Also used UIStepper to adjust values in another viewController.
I could see them being an issue is larger programs, and in my opinion the singleton thing is just a masked global. Keep it simple, if your app is simple that is.

What class of Objective-C variable is this?

I am working my way through some Objective-C code that I did not write and have found a variable declaration style that I am unfamiliar with. Can anyone tell me the scope of the variable 'myVar' in the class implementation below? Note that this appears in the '.m' file and not the interface declaration.
#implementation MyClass
#synthesize ivar1, ivar2;
NSString* myVar; // <- What is the intent?
- (id)init {
...
#end
To me the intention appears to be similar to that of a member variable. What are the advantages of declaring a variable in this way instead of using an ivar in the #interface declaration?
It's just a plain old global variable. There's only one instance of it, and it can be accessed by any code within the same file translation unit (the final file you get after running the preprocessor). Other translation units (that is, other .m files) can also access that global variable, but in order to do so, they need to use an extern statement:
extern NSString *myVar;
extern says "this is the name of a global variable, but it's defined in a different translation unit". The linker resolves all of the extern declarations at link time.
a poorly named global variable...
I'm not too experienced in ObjC but I'd say that is a global.