Class Extensions Explained

One of the features added to Objective-C 2.0 is Class Extensions.

If you have feature requests or bugs, is your friend!

There has been a bit of confusion about their functionality and purpose.

First, a bit of background. One of the great strengths of Objective-C is the very sharp division between interface and implementation.

Not only can you declare a public interface to a class — the set of methods that clients of the class can use — but you can also declare varying degrees of private interface ranging from “stuff for my framework” through to “stuff only to be used in the implementation of this subset of methods of the class”.

Class extensions were designed to solve two problems. The first was to enable to compiler to better validate the private interfaces a class might have and the second was to solve a subtle, but gnarly, problem with properties (another feature added to Objective-C 2.0).

A class extension is declared just like a category, but without a name:

@interface MyClass ()

The declarations found within the class extension directly extend the declarations found in the class’s primary @interface. In a limited context, the declarations in a class extension can override declarations found in the primary interface.

Better Validation of Private Interfaces

When implementing a class, it is common to have a set of methods that only appear in the class’s @implementation. Often, they are spread around the @implementation and generally appear just above whatever method first uses the private method (if they were to appear below, the compiler will warn).

Eventually, this becomes unwieldy and the developer will capture the private method’s declarations into a category declaration at the top of the implementation file. Something like:

@interface MyClass (SuperSecretInternalSauce)
- (void) doMyPrivateThing;
- (BOOL) canMyPrivateThingEatThis: (OtherClass *) aThing;

@implementation MyClass

These methods are typically not implemented in a corresponding @implementation MycClass (SuperSecretInternalSauce) implementation block. Nor should they need to be!

The resulting problem is that the compiler will not check to make sure you have implemented all of the methods declared in that category. Nor will it catch, say, spelling errors in the method declarations in the implementation.

This is because a category with no corresponding implementation is an informal protocol in Objective-C. It is a set of method declarations that can optionally be implemented, often on a subclass of the class with the category declaration.

Because a class extension effectively extends the class’s primary interface, changing the above declaration to the following makes the declared methods have the same requirements as methods declared in the class’s oft public primary interface.

@interface MyClass ()
- (void) doMyPrivateThing;
- (BOOL) canMyPrivateThingEatThis: (OtherClass *) aThing;

@implementation MyClass

That is, the compiler will complain if the class’s @implementation does not contain implementations of the methods declared in the extension.

Public Readonly, Private Readwrite Properties

When designing properties, one goal was to not make them fairly robust. To these ends, it was decided that property declarations in categories, synthesis in particular, would be limited in functionality or prohibited entirely.

Aside: The reason synthesis was prohibited in categories was because synthesis requires storage and there was no way of declaring storage in a category efficiently and it wasn’t deemed acceptable to allow a category to synthesize methods that would then diddle the class’s ivars directly. Too fragile and ugly.

However, it was also obviously desirable to be able to declare a property that was publicly readonly, but whose implementation was readwrite for internal-to-the-class-or-framework purposes.

One additional requirement is that the synthesis such properties must always be able to synthesize both the setter and getter naturally and precisely. Specifically, when declaring a property as atomic, there is no way the developer can correctly manually write only 1/2 of the getter setter pair; the locking infrastructure is not exposed and, thus, there is no way to guarantee atomicity in such a situation.

Class extensions addressed this problem elegantly.

Specifically, you can declare a property like:

@interface MyClass : NSObject
@property(readonly) NSView *targetView;

And, then, in the implementation file:

@interface MyClass()
@property(readwrite) NSView *targetView;

@implementation MyClass
@synthesize targetView;

End result? A property that is publicly readonly, but privately readwrite without opening properties up to all of the fun fragility associated with categories.

13 Responses to “Class Extensions Explained”

  1. Ahruman says:

    The distinction between interface and implementation is good, but it’s not as sharp as I’d like. There are two issues:
    * The need to include that dullest of implementation detail, ivar declarations, in the interface. This doesn’t seem necessary in the non-fragile runtime; it should be possible to put them in the main @implementation block. Yes, you can use synthesised properties with no declared ivar, but that’s not practical in all cases and besides mmalc keeps telling everyone this is a bug, not a feature. (rdar://5581983, dup/5277913)
    * The fact that the compilers complain if the main @interface’s readonly property declaration doesn’t declare the memory behaviour of the setter, but a class extension readwrite override does. (rdar://6588319, dup/7049802)

  2. bbum says:

    I said it was strong, not perfect. :)

    Yes– ivar declarations in the interface are annoying. The modern ABI lays the foundation for addressing that issue. So, it can be fixed. Obviously, I can’t say if or when it might be fixed.

    Well… the issue, though, is that assign/retain/copy are a part of the contract of the getter, in some cases, and do impact code generation. I agree that it is sub-optimal. It is just that it is a thornier issue to fix than one might first assume.

  3. Tim Wood says:

    We typically did put the private methods in an @implementation Foo (Private) block so that we’d get the checking, but I’m loving the ability to merge this into the main class with class extensions.

    My only gripe is the private-protocol problem (7217634, sorry, thought I’d logged this earlier), annoying but pretty minor next to all the goodness that class extensions give us. Thanks!

  4. Tweets that mention bbum’s weblog-o-mat » Blog Archive » Class Extensions Explained -- says:

    [...] This post was mentioned on Twitter by Mike Clark, JivaDeVoe and Matthew Tonkin. Mike Clark said: @bbum's "Class Extensions Explained" is another must-read for ObjC programmers: [...]

  5. Drew McCormack says:

    One thing I have wanted to do at various times is expose a immutable property, but back it with a mutable ivar. So I wanted this in the public interface

    @property (readonly) NSArray *array;

    and in the class extension I wanted to use this

    @property (readwrite) NSMutableArray *array;

    This doesn’t seem to be allowed by the compiler. Of course, you can get around it by simply renaming the ivar to mutableArray or something, but it seemed like something that should be possible (ie upcasting of a class in a property.

  6. bbum's weblog-o-mat » Blog Archive » Class Extensions Explained interface says:

    [...] Read the original post: bbum's weblog-o-mat » Blog Archive » Class Extensions Explained [...]

  7. Kyle Sluder says:

    Ahruman, are you sure about accessing synthesized ivars being a bug? I thought the bug was that one couldn’t get at the synthesized ivar and therefore had to implement -dealloc by calling setters. Now we can use self->ivar notation.

  8. Michael James says:

    Found this out through experimentation:

    To access the readwrite property from subclasses, re-declare the property as readwrite in your subclass’s class extension and use “@dynamic propertyName” in the @implementation section. Seems to work.

    Sorry for the typos.

  9. Jonathan Mitchell says:

    Class extensions come galloping to the rescue when you turn on GCC_WARN_UNDECLARED_SELECTOR.
    All those callback message selectors that you forgot to declare anywhere can be decanted into the extension without polluting the public interface.

  10. Dan says:

    One of the very few things I dislike about Obj-C over other languages is the lingering need for header files (unlike Java, Python, etc.). But I’ve recently thought of an approach that, while not making them unnecessary, at least would reduce the back-and-forth between editing the two files, somewhat. However, I’m wondering if it will lead to unexpected problems.
    The approach would be to put the only the class extension @interface in the header, declaring the methods and properties I want other classes to be aware of (thus “public”), while the interface containing the ivars and private methods would be in the implementation (.m) file. Is this plan “so crazy, it just might work!”, or a “bag of hurt ™”? Secondly, if it would work, would Clang’s static analyzer have trouble understanding it? Would code sense?
    Lastly, I’d like to thank you for your profuse and generous contributions to the Cocoa development community’s knowledge base! It really is appreciated by so many of us.

  11. bbum says:

    Dan — it is a good idea, but won’t work in practice. A class extension doesn’t declare the superclass and, thus, the compiler wouldn’t know about any inherited methods when it only saw the extension. It would also be incompatible with the 32 bit Mac OS X runtime in that the compiler has to see the instance variables of the super classes to be able to correctly layout ivars in the current class.

    However, if you are willing to be “limited” to 64 bit and iPhone OS only (the only downside being lack of compatibility with the simulator), you can achieve what you want today using a combination of synthesized properties (that also synthesize ivars) and class extensions at the top of the .m to hold private properties and method declarations.

  12. Dan says:

    Darn. And thanks, but darn. I guess it’s plan B then: put all of my source code into one file. (j/k)

  13. Nimrod says:

    Great explanation. Thanks! This was very useful.

Leave a Reply

Line and paragraph breaks automatic.
XHTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>