Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declaration/definition of variables locations in ObjectiveC?

People also ask

How do you declare a variable in Objective C?

Variable Declaration in Objective-C You will use extern keyword to declare a variable at any place. Though you can declare a variable multiple times in your Objective-C program but it can be defined only once in a file, a function or a block of code.

Can we declare variables anywhere in C?

Modern C compilers such as gcc and clang support the C99 and C11 standards, which allow you to declare a variable anywhere a statement could go. The variable's scope starts from the point of the declaration to the end of the block (next closing brace). You can also declare variables inside for loop initializers.

Where is the best place to declare variables?

The recommended practice is to put the declaration as close as possible to the first place where the variable is used. This also minimizes the scope. From Steve McConnell's "Code Complete" book: Ideally, declare and define each variable close to where it's first used.

What is declaration and definition of a variable?

Declaration of a variable is for informing the compiler of the following information: name of the variable, type of value it holds, and the initial value if any it takes. i.e., declaration gives details about the properties of a variable. Whereas, Definition of a variable says where the variable gets stored.


I can understand your confusion. Especially since recent updates to Xcode and the new LLVM compiler changed the way ivars and properties can be declared.

Before "modern" Objective-C (in "old" Obj-C 2.0) you didn't have a lot of choices. Instance variables used to be declared in the header between the curly brackets { }:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}
@end

You were able to access these variables only in your implementation, but not from other classes. To do that, you had to declare accessor methods, that look something like this:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}

- (int)myVar;
- (void)setMyVar:(int)newVar;

@end


// MyClass.m
@implementation MyClass

- (int)myVar {
   return myVar;
}

- (void)setMyVar:(int)newVar {
   if (newVar != myVar) {
      myVar = newVar;
   }
}

@end

This way you were able to get and set this instance variable from other classes too, using the usual square bracket syntax to send messages (call methods):

// OtherClass.m
int v = [myClass myVar];  // assuming myClass is an object of type MyClass.
[myClass setMyVar:v+1];

Because manually declaring and implementing every accessor method was quite annoying, @property and @synthesize were introduced to automatically generate the accessor methods:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}
@property (nonatomic) int myVar;
@end

// MyClass.m
@implementation MyClass
@synthesize myVar;
@end

The result is much clearer and shorter code. The accessor methods will be implemented for you and you can still use the bracket syntax as before. But in addition, you can also use the dot syntax to access properties:

// OtherClass.m
int v = myClass.myVar;   // assuming myClass is an object of type MyClass.
myClass.myVar = v+1;

Since Xcode 4.4 you don't have to declare an instance variable yourself anymore and you can skip @synthesize too. If you don't declare an ivar, the compiler will add it for you and it will also generate the accessor methods without you having to use @synthesize.

The default name for the automatically generated ivar is the name or your property starting with an underscore. You can change the generated ivar's name by using @synthesize myVar = iVarName;

// MyClass.h
@interface MyClass : NSObject 
@property (nonatomic) int myVar;
@end

// MyClass.m
@implementation MyClass
@end

This will work exactly as the code above. For compatibility reasons you can still declare ivars in the header. But because the only reason why you would want to do that (and not declare a property) is to create a private variable, you can now do that in the implementation file as well and this is the preferred way.

An @interface block in the implementation file is actually an Extension and can be used to forward declare methods (not needed anymore) and to (re)declare properties. You could for instance declare a readonly property in your header.

@property (nonatomic, readonly) myReadOnlyVar;

and redeclare it in your implementation file as readwrite to be able to set it using the property syntax and not only via direct access to the ivar.

As for declaring variables completely outside of any @interface or @implementation block, yes those are plain C variables and work exactly the same.


First, read @DrummerB's answer. It a good overview of the whys and what you should generally do. With that in mind, to your specific questions:

#import <Foundation/Foundation.h>

// 1) What do I declare here?

No actual variable definitions go here (it's technically legal to do so if you know exactly what you're doing, but never do this). You may define several other kinds of things:

  • typdefs
  • enums
  • externs

Externs look like variable declarations, but they're just a promise to actually declare it somewhere else. In ObjC, they should only be used to declare constants, and generally only string constants. For instance:

extern NSString * const MYSomethingHappenedNotification;

You would then in your .m file declare the actual constant:

NSString * const MYSomethingHappenedNotification = @"MYSomethingHappenedNotification";

@interface SampleClass : NSObject
{
    // 2) ivar declarations
    // Pretty much never used?
}

As noted by DrummerB, this is legacy. Don't put anything here.


// 3) class-specific method / property declarations

@end

Yep.


#import "SampleClass.h"

// 4) what goes here?

External constants, as described above. Also file static variables can go here. These are the equivalent of class variables in other languages.


@interface SampleClass()

// 5) private interface, can define private methods and properties here

@end

Yep


@implementation SampleClass
{
    // 6) define ivars
}

But very rarely. Almost always you should allow clang (Xcode) to create the variables for you. The exceptions are usually around non-ObjC ivars (like Core Foundation objects, and especially C++ objects if this is an ObjC++ class), or ivars that have weird storage semantics (like ivars that don't match with a property for some reason).


// 7) define methods and synthesize properties from both public and private
//    interfaces

Generally you shouldn't @synthesize anymore. Clang (Xcode) will do it for you, and you should let it.

Over the last few years, things have gotten dramatically simpler. The side-effect is that there are now three different eras (Fragile ABI, Non-fragile ABI, Non-fragile ABI + auto-syntheisze). So when you see the older code, it can be a little confusing. Thus confusion arising from simplicity :D


I'm also pretty new, so hopefully I don't screw anything up.

1 & 4: C-style global variables: they have file wide scope. The difference between the two is that, since they're file wide, the first will be available to anyone importing the header while the second is not.

2: instance variables. Most instance variables are synthesized and retrieved/set through accessors using properties because it makes memory management nice and simple, as well as gives you easy-to-understand dot notation.

6: Implementation ivars are somewhat new. It's a good place to put private ivars, since you want to only expose what's needed in the public header, but subclasses don't inherit them AFAIK.

3 & 7: Public method and property declarations, then implementations.

5: Private interface. I always use private interfaces whenever I can to keep things clean and create a kind of black box effect. If they don't need to know about it, put it there. I also do it for readability, don't know if there are any other reasons.


This is an example of all kinds of variables declared in Objective-C. The variable name indicate its access.

File: Animal.h

@interface Animal : NSObject
{
    NSObject *iProtected;
@package
    NSObject *iPackage;
@private
    NSObject *iPrivate;
@protected
    NSObject *iProtected2; // default access. Only visible to subclasses.
@public
    NSObject *iPublic;
}

@property (nonatomic,strong) NSObject *iPublic2;

@end

File: Animal.m

#import "Animal.h"

// Same behaviour for categories (x) than for class extensions ().
@interface Animal(){
@public
    NSString *iNotVisible;
}
@property (nonatomic,strong) NSObject *iNotVisible2;
@end

@implementation Animal {
@public
    NSString *iNotVisible3;
}

-(id) init {
    self = [super init];
    if (self){
        iProtected  = @"iProtected";
        iPackage    = @"iPackage";
        iPrivate    = @"iPrivate";
        iProtected2 = @"iProtected2";
        iPublic     = @"iPublic";
        _iPublic2    = @"iPublic2";

        iNotVisible   = @"iNotVisible";
        _iNotVisible2 = @"iNotVisible2";
        iNotVisible3  = @"iNotVisible3";
    }
    return self;
}

@end

Note that the iNotVisible variables are not visible from any other class. This is a visibility issue, so declaring them with @property or @public doesn't change it.

Inside a constructor it's good practice to access variables declared with @property using underscore instead self to avoid side effects.

Let's try to access the variables.

File: Cow.h

#import "Animal.h"
@interface Cow : Animal
@end

File: Cow.m

#import "Cow.h"
#include <objc/runtime.h>

@implementation Cow

-(id)init {
    self=[super init];
    if (self){
        iProtected    = @"iProtected";
        iPackage      = @"iPackage";
        //iPrivate    = @"iPrivate"; // compiler error: variable is private
        iProtected2   = @"iProtected2";
        iPublic       = @"iPublic";
        self.iPublic2 = @"iPublic2"; // using self because the backing ivar is private

        //iNotVisible   = @"iNotVisible";  // compiler error: undeclared identifier
        //_iNotVisible2 = @"iNotVisible2"; // compiler error: undeclared identifier
        //iNotVisible3  = @"iNotVisible3"; // compiler error: undeclared identifier
    }
    return self;
}
@end

We can still access the not visible variables using the runtime.

File: Cow.m (part 2)

@implementation Cow(blindAcess)

- (void) setIvar:(NSString*)name value:(id)value {
    Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
    object_setIvar(self, ivar, value);
}

- (id) getIvar:(NSString*)name {
    Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
    id thing = object_getIvar(self, ivar);
    return thing;
}

-(void) blindAccess {
    [self setIvar:@"iNotVisible"  value:@"iMadeVisible"];
    [self setIvar:@"_iNotVisible2" value:@"iMadeVisible2"];
    [self setIvar:@"iNotVisible3" value:@"iMadeVisible3"];
    NSLog(@"\n%@ \n%@ \n%@",
          [self getIvar:@"iNotVisible"],
          [self getIvar:@"_iNotVisible2"],
          [self getIvar:@"iNotVisible3"]);
}

@end

Let's try to access the not visible variables.

File: main.m

#import "Cow.h"
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
    @autoreleasepool {
        Cow *cow = [Cow new];
        [cow performSelector:@selector(blindAccess)];
    }
}

This prints

iMadeVisible 
iMadeVisible2 
iMadeVisible3

Note that I was able to access the backing ivar _iNotVisible2 which is private to the subclass. In Objective-C all variables can be read or set, even those that are marked @private, no exceptions.

I didn't include associated objects or C variables as they are different birds. As for C variables, any variable defined outside @interface X{} or @implementation X{} is a C variable with file scope and static storage.

I didn't discuss memory management attributes, or readonly/readwrite, getter/setter attributes.