There are subtle issues around #import order. You may not believe me — until you try reusing code in a new project.
In Wild #imports! we looked at the problems caused by having too many #import directives. But it’s also possible that you have too few, resulting in bad header files — especially if you don’t pay attention to #import order in your .m files.
When it comes to imports, header files should satisfy these two conditions:
“Minimal” just means a header file should import no more than it needs.
“Complete” means the header file imports everything that needed to compile it. Consider
#import "foo.h" #import "bar.h"
If removing foo.h (or changing the order) causes bar.h not to compile, then bar.h is not complete.
One way to have an incomplete header is by relying on precompiled headers. In particular, just because the precompiled headers include a particular header doesn’t mean you can omit it elsewhere.
Another way to have an incomplete header is with a poor #import order that masks dependencies. In C-based languages, programmers often begin their implementation files by including the most general headers from the largest scope. They work down from there, until they reach the most specific headers:
This is backwards. Consider a header foo.h that depends on <QuartzCore/QuartzCore.h>. If foo.m imports QuartzCore first, then other stuff, then finally gets to its own header, you may not feel the need to import QuartzCore in foo.h. …And this will break for the next programmer who comes along and just imports foo.h.
Disclosure: The book links below are affiliate links. If you buy anything, I earn a commission, at no extra cost to you.
The solution is simple: reverse the order! Start from the most specific, then work towards the most general. Most importantly, include your own header first. Large-Scale C++ Software Design by John Lakos is the only book I know about “physical design” — how to arrange source code into files.
The author states,
Latent usage errors can be avoided by ensuring that the .h file of a component parses by itself – without externally-provided declarations or definitions… Including the .h file as the very first line of the .c file ensures that no critical piece of information intrinsic to the physical interface of the component is missing from the .h file (or, if there is, that you will find out about it as soon as you try to compile the .c file).
In other words, by including your own header first… if the header is not complete, you will fail fast!
Here’s what I do. If I’m writing foo.m, I first import foo.h. I keep this separated from other imports by a blank line. All the others follow, in sorted order:
#import "foo.h" #import "abc.h" #import "def.h" #import <Abc/Abc.h>
Sorting helps me find duplicates. It also puts angle brackets imports <> after quoted imports, so that the most general headers come last.
Questions: What ordering do you use for your #import statements? What tools do you use to help with your ordering? Leave a comment below.
Programming was fun when I was a kid. But working in Silicon Valley, I saw poor code lead to fear, with real human costs. Looking for ways to make my life better, I learned about Design Patterns, Refactoring, and Test-Driven Development (TDD). Programming became fun again! I've now been doing TDD in Apple environments for 18 years. I'm committed to software crafting as a discipline, hoping we can all reach greater effectiveness and joy.
Please log in again. The login page will open in a new tab. After logging in you can close it and return to this page.