views:

268

answers:

3

I've no idea why, but on occasion I've managed to fix some compile errors, most notably

error expected specifier-qualifier-list before 'someClass'

by moving #import "someClass.h" from the .h file into the .m file. This has also worked with a couple of other problems i've encountered that have been (mysteriously from my point of view) related to headers.

Some cursory googling has turned up the answer "never import headers in header file" and that's where the advice stops.

Either I've completely made this up, or I've picked up the habit from somewhere, but I thought the header was where headers were meant to be imported. Clearly not, but can anyone explain to me why that is, and what's the preferred way of importing headers?

+9  A: 

Unless you're inheriting from the class that you're including, you shouldn't include headers in headers. If you need to include an object as an interface variable, you should use the @class directive instead; that'll tell the compiler that the identifier refers to a class.

Instead, import headers only in implementation files. The compiler will know that your instance variables are pointers to objects, but it doesn't know the details of the object when parsing the header. All it needs to know is that it's a class. The compiler can then see the methods of the class when parsing your implementation file; at that point, it does need the class, to verify that it responds to the messages you're sending.


Update: I was going to update my answer to respond to some later questions, but Rob Napier has a good follow-up.

John Feminella
What about `typedef`s and `protocols`?
JoostK
you wouldn't happen to know of a tutorial, or a code sample that uses this?
gargantaun
+10  A: 

John gives good advice, but here is a bit more background on the whys, wherefores and exceptions.

There are two goals behind avoiding importing headers into headers: improved incremental build times and the avoidance of circular dependencies. If you import A.h into B.h and import B.h into C.h, then every time you change anything in A.h, you have to recompile C.m, even if C.m makes no use of any of the things defined in A.h. This can lead to really terrible and unnecessary build-churn, especially if you have headers that change often (as is common in the early stages of development).

The first goal is laudable, but for smallish projects, who cares? You've got a quad-core Pro and a complete build takes a couple of minutes, right? But you still have to worry about the second problem: circular dependencies. A.h references class B and B.h references class A. This actually can happen quite often and can creep into a system quite innocently. A collection object might reference the type of the objects it contains, and the objects might reference the type of the collection object. All it takes is a single reference because of some method that takes or returns that type. If you have headers importing other headers, the likelihood of this happening rapidly approaches unity. You wind up with recursive imports, and the compile-time errors can be mind-blowing. "I know that typdef is defined! It's right there! It's imported!" But, it wasn't parsed yet when you imported this header. This is what's causing your error above.

For this reason, even if you don't care that much about build times (though you should), avoid importing headers into headers... except....

Some headers you have to import. Your superclass of course. Files that define a @protocol you implement or typedef you use. So yeah, you have to include those.

And what about system headers? Well, they're never going to cause churn, and obviously they're not going to cause recursive imports, so they're fine. I discourage folks from using @class forward declarations for things in system headers. It creates extra work on the user of your header for no value. For good header hygiene, please remember to enclose system headers in <angle brackets> and your headers in "quotation marks".

So it's not a trivial question, but the simple rule is: avoid importing user headers into other user headers anytime the compiler will let you.

Rob Napier
thank you very much for that.
gargantaun
A: 

You'll not only include your header into your implementation file but into other files using that class as well. If you include all dependencies in the header, all other files including that header just for using the specific class it defines will themselves include all dependencies as well.

This is not only noisy but leads to problems when you have classes referencing themselves. Each class would have to be imported before the other one, which leads to one being imported first and then cannot finding the other class' type. This results in the cryptic error message you posted, which basically means that the compiler can't find the someClass type.

By moving your imports into your implementation file and forward-declaring classes and types in your header (using @class, @protocol, etc) you can avoid these issues.

Adrian