#import
includes the content of the header in the source.
Thus, every declaration which is in the imported header is also imported.
@class
only declares to the compiler that the given class exists, but does not import the header itself. It is called a forward declaration, as you only declares to the compiler that the class exists before defining it in details (telling which methods it implements and so on)
Consequences:
- When using
#import
in your .m
file, if the header is modified, it will trigger the recompilation of the .m
file that #import
it on next compilation. Instead, if you use @class
, your .m
does not depend on the header and if the header is modified, the .m
file is not recompiled.
- Using
@class
also avoid cross-imports, e.g. if the class A references class B and class B references class A, then you can't #import "A.h"
in B.h and #import B.h
in A.h in the same time (it would be an "import infinite loop")
- Using
@class
only declare that a class exists and does not tell the compiler which methods the class responds to.
This is why usually the best practice is to forward-declare the class using @class A
in the header (.h) files that references class A, just so that the compiler knows that "A" is a known class but doesn't need to know more, and #import "A.h"
in the implementation (.m) file so that you can call methods on the objet of class A in your source file.
In addition to avoid import loops, this will also avoid to recompile files if they don't need to, and thus reduce your compile time.
The only exceptions are when the declaration of your class inherits another class, or when it declares that it conforms to a given @protocol
(like delegate protocols and so on), because in this particular case, the compiler needs you to #import
the whole definition of the parent class or @protocol
(to know if your class correctly conforms to this given protocol).
MyClassA.h
// Tells the compiler that "MyClassB" is a class, that we will define later
@class MyClassB; // no need to #import the whole class, we don't need to know the whole definition at this stage
@interface MyClassA : NSObject {
MyClassB* someB; // ok, the compiler knows that MyClassB is a class, that's all it needs to know so far
}
-(void)sayHello;
-(void)makeBTalk;
@end
MyClassB.h
@class MyClassA; // forward declaration here too
// anyway we couldn't #import "MyClassA.h" here AND #import "MyClassB.h" in MyClassA.h as it would create an unsolvable import loop for the compiler
@interface MyClassB : NSObject {
MyClassA* someA; // ok, the compiler knows that MyClassA is a class, that's all it needs to know so far
}
-(void)talk;
-(void)makeABePolite;
@end
MyClassA.m
// import MyClassB so that we know the whole definition of MyClassB, including the methods it declares
#import "MyClassB.h" // thus we here know the "-talk" method of MyClassB and we are able to call it
@implementation MyClassA
-(void)sayHello { NSLog(@"A says Hello"); }
-(void)makeBTalk {
[someB talk];
// we can call the 'talk' method because we #imported the MyClassB header and knows this method exists
}
@end
MyClassB.m
// import MyClassA so that we know the methods it declares and can call them
#import "MyClassA.h"
@implementation MyClassB
-(void)talk { NSLog(@"B is talking"); }
-(void)makeABePolite {
[someA sayHello];
// we can call this because we #import MyClassA
}
@end
PS: Note that if this is a best practice, I know a lot of developers (including myself sometimes ^^) that #import
the header it needs in their .h files, instead of only forward-declare it using @class
... this is some bad habit — or because these developers doesn't know these subtleties — that you will unfortunately encounter in existing code anyway.