Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cocoa: What's the difference between importing in the header and importing in the main file?

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?

like image 207
gargantuan Avatar asked Dec 15 '09 13:12

gargantuan


2 Answers

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.

like image 88
Rob Napier Avatar answered Sep 21 '22 19:09

Rob Napier


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.

like image 22
John Feminella Avatar answered Sep 23 '22 19:09

John Feminella