Declaration/definition of variables locations in ObjectiveC?

Alexandr Kurilin picture Alexandr Kurilin · Sep 28, 2012 · Viewed 53.4k times · Source

Ever since starting to work on iOS apps and objective C I've been really puzzled by the different locations where one could be declaring and defining variables. On one hand we have the traditional C approach, on the other we have the new ObjectiveC directives that add OO on top of that. Could you folks helps me understand the best practice and situations where I'd want to use these locations for my variables and perhaps correct my present understanding?

Here's a sample class (.h and .m):

#import <Foundation/Foundation.h>

// 1) What do I declare here?

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

// 3) class-specific method / property declarations

@end

and

#import "SampleClass.h"

// 4) what goes here?

@interface SampleClass()

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

@end

@implementation SampleClass
{
    // 6) define ivars
}

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

@end
  • My understanding of 1 and 4 is that those are C-style file-based declarations and definitions that have no understanding whatsoever of the concept of class, and thus have to be used exactly how they would be used in C. I've seen them used for implementing static variable-based singletons before. Are there other convenient uses I'm missing?
  • My take from working with iOS is that ivars have been alost completely phased out outside of the @synthesize directive and thus can be mostly ignored. Is that the case?
  • Regarding 5: why would I ever want to declare methods in private interfaces? My private class methods seem to compile just fine without a declaration in the interface. Is it mostly for readability?

Thanks a bunch, folks!

Answer

DrummerB picture DrummerB · Sep 28, 2012

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.