Vincent Gable’s Blog

December 4, 2008

Cocoa Coding Style Minutia: All Forward Declarations on One Line is the One True Way

This is a very petty point, but I believe there’s a right answer, and since it’s come up before I’m going to take the time to justify the best choice.

Forward Declare Classes on One Line, Not Many Lines

Right:
@class Foo, Bar, Baz...;

This is the style that Apple’s headers generally follow, look at NSDocument.h for example.

Wrong:
@class Foo;
@class Bar;
@class Baz;
...

Programming languages are to be read by people first, and interpreted by computers second. I really do hope anyone in software engineering can take that as an axiom!

Therefore, always favor brevity above all else with statements that are only for the compiler. They are by far the least important part of the code base. Keeping them terse minimizes distractions from code that can and should be read.

Naturally, it’s very hard to find a statement that’s only for the compiler. I can only think of one such statement in Objective-C, forward declarations of an object with @class.

What Does @class Do?

@class Foo; tells the compiler that Foo is an Objective-C class, so that it knows how much space to reserve in memory for things of type Foo. It does not tell the compiler what methods and ivars a Foo has, to do that you need to #import "Foo.h" (or have an @interface Foo... in your .m file.)

@class Foo; #import "Foo.h"
Foo is a black box. You can only use it as an argument. You can also send messages to Foo.

What is @class Good For?

Since #import "Foo.h" does everything @class Foo; does and more, why would you ever use @class? The short answer is less time wasted compiling.

Lets say you have a Controller class, and it has an ivar that’s a Foo. To get it to compile, you put #import "Foo.h" inside Controller.h. So far so good. The problem comes when Foo.h is changed. Now any file that has #import "Foo.h" in it must be recompiled. So Controller.h has to be recompiled. So any file that has #import "Controller.h" in it must also be recompiled, and so on. Many objects that don’t use Foo objects, but do talk to the Controller (or to something that talks to something that talks to the Controller!) have to be rebuilt as well. It’s likely that the entire project would end up being rebuilt. With even a moderately sized project, that means a lot of needless compile time!

One solution is to put a forward-declaration, @class Foo;, in Controller.h, and #import "Foo.h" in Controller.m, and the few files that actually do something to Foo objects. @class Foo; gives the compiler just enough information to build an object with a Foo* ivar, because it knows how much space that ivar will need in memory. And since only objects that need to talk to Foo objects directly have any dependency on Foo.h, changes to Foo can be tested quickly. The first time I tried this solution in one of my projects, compile times dropped from minutes to seconds.

Readers Need Less @class

Forward declarations add value to the development process, as does anything that saves programmers time. But @class tells a human reader nothing. Which is why I say you should use them in your code, but minimize the space they take up, by putting them all on one line.

@class Foo; tells a reader that “Foo is black-box that is a class.” That adds no useful information.

If the reader does not care about what a Foo is (they are treating it as a black box), then knowing that Foo is a class is useless information. Foo could be a struct, or a bit-field, or a function pointer, or anything else and they still would not need to know to understand the code.

Conversely, if they need to know what a Foo is to understand the code, then they need to know more then “it is a class”. They need to see Foo.h or documentation — @class just isn’t enough.

Exceptions

I want to again emphasize that this isn’t a big deal. I strongly feel that putting all forward declarations on one line is the right answer, but doing it wrong won’t perceptibly matter in the end.

So I don’t think there’s any reason to address exceptions to the rule. Do what you gotta do.

Best Practices

Use @class to speed up compile-times as much as you can, but use the keyword @class as little as possible, since it is for the compiler, not the person reading the code.

No Comments »

No comments yet.

RSS feed for comments on this post.

Leave a comment

Powered by WordPress