Where does it go, George?

Mark Dalrymple's Headshot
Mark Dalrymple

Today's topic was suggested by Paul Bruneau - thanks!

In case you've lost count, we have three places we can declare instance variables in modern Objective-C:

The first is in the class interface, as it always has been:

@interface BNRThingie : NSObject {
    NSString *_thingie1;
    int _thingie2;
@end // BNRThingie

The second is in the class extension:

@interface BNRThingie () {
    NSString *_extensionThingie1;
    int _extensionThingie2;
@end // extension

The third is at the start of the class implementation:

@implementation BNRThingie {
    NSString *_implementationThingie1;
    int _implementationThingie2;

@end // BNRThingie

No matter where you declared your instance variables, you can access them in the class:

- (NSString *) description {
    NSString *desc = [NSString stringWithFormat: @"<%@:%p %@ %d %@ %d %@ %d>",
                               [self class], self,
                               _thingie1, _thingie2,
                               _extensionThingie1, _extensionThingie2,
                               _implementationThingie1, _implementationThingie2];
    return desc;
} // description

This brings up the question: "Where should I put my instance variables?" For the most part, you'll probably make @properties and use @synthesize to create the backing ivar. (You can read more about that at A Motivaton for ivar decorations) The development version of clang supports auto-synthesis of instance variables, so you won't even have to do @synthesize.

If you have to support older compilers (such as gcc) you'll need to put the instance variables in the @interface. Similarly, older versions of Xcode don't have the debugger support for ivars declared anywhere else but the @interface. If you want to see your ivars in Xcode's debugger panels, you'd need to put ivars in the @interface. Luckily newer Xcodes don't have this limitation.

If you have public instance variables, part of your public interface, they'd go into the @interface as well. This probably isn't a wise design idea, but it might be something you have to do.

If your instance variables are private, purely implementation details, put them into the implementation. It still feels weird to put them there, but I've been doing Cocoa long enough that I have some ingrained habits to break:

@implementation SomeThing {
    SomeThingTrackingMode _trackingMode;
    CGPoint _anchorPoint, _dragPoint;

- (id) initWithPancake: (void *) waffle {

You can also put them into the class extension if it feels better to you to consolidate the meta / declaratory stuff in one place.

One thing that's kind of cool is you can declare instance variables in a class extension, then include that class extension (via another header file) into a subclass implementation. You'll need to make the class extension instance variables @protected or @public so your subclasses can have access to them. This allows you to keep your public header very clean (no instance variables), and have a secondary header with the class extension that's used by test code and/or subclasses.

Another thing that's kind of cool is you can put your IBOutlet decorations on instance variables declared in a class extension or in the implementation, as well as decorations on @properties that live in class extensions. Interface Builder will pick up on the outletitude and let you make connections, but also keep them out of the public header file if they're just implementation details. They'd look something like this:

@interface SomeThing () {
    IBOutlet NSView *_extensionView;
@property (strong) IBOutlet NSView *extensionProperty;

@end // extension

@implementation SomeThing {
    IBOutlet NSView *_implementationView;

And would be available in Xcode like this:

Plethora of Outlets

Here are my rules of thumb for ivar location:

  • For older compilers or older Xcodes that don't fully support non-interface ivars, I put them into the @interface

  • If it's a public ivar, into @interface it goes (I haven't actually had to use this rule yet)

  • If it's a private implementation detail, I put it into the @implementation. Sometimes I put them in the class extension. I haven't totally Searched My Feelings on this one.

  • If it's something to be exposed to subclasses or to tests, it goes into a class extension in a separate header file.

Fascinated by the ins and outs of Objective-C? The Advanced Mac Bootcamp covers the language in extensive detail.

Recent Comments

comments powered by Disqus