It is to my understanding that one should use a forward-class declaration in the event ClassA needs to include a ClassB header, and ClassB needs to include a ClassA header to avoid any circular inclusions. I also understand that an #import
is a simple ifndef
so that an include only happens once.
My inquiry is this: When does one use #import
and when does one use @class
? Sometimes if I use a @class
declaration, I see a common compiler warning such as the following:
warning: receiver 'FooController' is a forward class and corresponding @interface may not exist.
Would really love to understand this, versus just removing the @class
forward-declaration and throwing an #import
in to silence the warnings the compiler is giving me.
The common practice is using @class in header files (but you still need to #import the superclass), and #import in implementation files. This will avoid any circular inclusions, and it just works.
Simple answer: You
#import
or#include
when there is a physical dependency. Otherwise, you use forward declarations (@class MONClass
,struct MONStruct
,@protocol MONProtocol
).Here are some common examples of physical dependence:
CGPoint
as an ivar or property, the compiler will need to see the declaration ofCGPoint
.The compiler's actually very lenient in this regard. It will drop hints (such as the one above), but you can trash your stack easily if you ignore them and don't
#import
properly. Although it should (IMO), the compiler does not enforce this. In ARC, the compiler is more strict because it is responsible for reference counting. What happens is the compiler falls back on a default when it encounters an unknown method which you call. Every return value and parameter is assumed to beid
. Thus, you ought to eradicate every warning from your codebases because this should be considered physical dependence. This is analogous to calling a C function which is not declared. With C, parameters are assumed to beint
.The reason you would favor forward declarations is that you can reduce your build times by factors because there is minimal dependence. With forward declarations, the compiler sees there is a name, and can correctly parse and compile the program without seeing the class declaration or all of its dependencies when there is no physical dependency. Clean builds take less time. Incremental builds take less time. Sure, you will end up spending a little more time making sure the all the headers you need are visible to every translation as a consequence, but this pays off in reduced build times quickly (assuming your project is not tiny).
If you use
#import
or#include
instead, you're throwing a lot more work at the compiler than is necessary. You're also introducing complex header dependencies. You can liken this to a brute-force algorithm. When you#import
, you're dragging in tons of unnecessary information, which requires a lot of memory, disk I/O, and CPU to parse and compile the sources.ObjC is pretty close to ideal for a C based language with regards to dependency because
NSObject
types are never values --NSObject
types are always reference counted pointers. So you can get away with incredibly fast compile times if you structure your program's dependencies appropriately and forward where possible because there is very little physical dependence required. You can also declare properties in the class extensions to further minimize dependence. That's a huge bonus for large systems -- you would know the difference it makes if you have ever developed a large C++ codebase.Therefore, my recommendation is to use forwards where possible, and then to
#import
where there is physical dependence. If you see the warning or another which implies physical dependence -- fix them all. The fix is to#import
in your implementation file.As you build libraries, you will likely classify some interfaces as a group, in which case you would
#import
that library where physical dependence is introduced (e.g.#import <AppKit/AppKit.h>
). This can introduce dependence, but the library maintainers can often handle the physical dependencies for you as needed -- if they introduce a feature, they can minimize the impact it has on your builds.Forward declaration just to the prevent compiler from showing error.
the compiler will know that there is class with the name you've used in your header file to declare.
Think of @class as telling the compiler "trust me, this exists".
Think of #import as copy-paste.
You want to minimize the number of imports you have for a number of reasons. Without any research, the first thing that comes to mind is it reduces compile time.
Notice that when you inherit from a class, you can't simply use a forward declaration. You need to import the file, so that the class you're declaring knows how it's defined.
Use a forward declaration in the header file if needed, and
#import
the header files for any classes you're using in the implementation. In other words, you always#import
the files you're using in your implementation, and if you need to reference a class in your header file use a forward declaration as well.The exception to this is that you should
#import
a class or formal protocol you're inheriting from in your header file (in which case you wouldn't need to import it in the implementation).Another advantage: Quick compilation
If you include a header file, any change in it causes the current file also to compile but this is not the case if the class name is included as
@class name
. Of course you will need to include the header in source file