Instance Variables in Objective-C

If you’ve ever taken a peek inside Apple’s Objective-C headers, you might have noticed some interesting things about the way instance variables are declared, e.g.:

@interface NSWindowController : NSResponder <NSCoding> {
    @private
    . . .
    struct __wcFlags {
    . . .
	    unsigned int RESERVED:28;
    } _wcFlags;
    id _moreVars;
}

Why does Apple bother padding the struct with reserved bytes? And why store extra instance variables in a separate object altogether? To answer that, we have to understand how the runtime works.

The Traditional Runtime

Traditionally, in the “fragile” runtime, objects were treated very similarly to structs, with the compiler hardcoding the instance variables’ offsets into the executable code. This requires the compiler to know about every instance variable ahead of time. There are two direct effects of this requirement:

  1. The compiler needs to have easy access to a list of all ivars, so the language requires they be listed in your public header. No exceptions.
  2. If you change the number (or, in some cases, type) of ivars of a class, all subclasses must be recompiled to ensure they access their ivars with the correct offsets.

So, why does Apple make use of the tricks above? Backwards compatibility. They want to ensure that they can extend their classes in the future without breaking your code.

Non-fragile Runtime to the Rescue

Thankfully, Apple removed this limitation with the introduction of a new runtime, which is used by all iOS apps and 64bit Mac apps targeting 10.5 or higher. Without going into too much detail, it leverages the runtime to dynamically update instance variables’ offsets, adding robustness and easing the burden of the compiler. This mechanism has also made possible a few compiler enhancements:

  1. Synthesized properties automatically create instance variables

    By now, you are most likely aware of this feature. When you synthesize a declared property, the compiler will automatically create an instance variable for you.

    @interface MyClass : NSObject
    @property(copy) NSString *name;
    @end
    
    @implementation MyClass
    @synthesize name;
    @end
    

  2. Truly private instance variable declarations

    We can finally move our private instance variables out of our public headers. In addition to being able to declare them in our public header, they can now be placed in a class extension:

    @implementation MyClass () {
        id mySuperPrivateVariable;
    }
    . . .
    @end
    

    or the regular implementation itself:

    @implementation MyClass {
        id mySuperPrivateVariable2;
    }
    . . .
    @end
    

Conclusion

Although the modern runtime has been available for some time, and you have likely already been taking advantage of it, it’s still useful to understand what makes these language features possible. I also believe that we can expect to see the compiler and developer tools improve into the future thanks to the added flexibility it has provided.