Using the @class Directive

The @class directive often seems to be a point of confusion. Let me try and provide a little insight.

The @class directive sets up a forward reference to another class. For example, in the code below the reference to @class HomeBrewRecipes informs the compiler that HomeBrewRecipes is indeed a class, so when the compiler gets to line 10 no additional information is needed, it assumes all is well and forges ahead.

1
2
3
4
5
6
7
8
9
10
11
//
//  HomeBrewBook.h
//
 
@class HomeBrewRecipes;
 
@interface HomeBrewBook : NSObject  
{  
  int  pageCount;
  HomeBrewRecipes  *recipes;  
}

When HomeBrewRecipes class is actually referenced, for example an instance of the class is created, the class interface will need to be imported – put another way, the class that creates an instance of HomeBrewRecipes will need to include HomeBrewRecipes.h, as is shown in the example below:

//
//  HomeBrewBook.m
//
 
#import "HomeBrewRecipes.h"
 
@implementation HomeBrewBook
 
- (void)someMethod
{ 
  recipes = [[HomeBrewRecipes alloc] init];
 
  ...
}
 
@end
What Not Just Always Import?

In many cases you can import the interface file and achieve the same end result.

 
#import "HomeBrewRecipes.h"
 
@interface HomeBrewBook : NSObject  
{  
  int  pageCount;
  HomeBrewRecipes  *recipes;  
}

So why bother with @class? The primary reason is to avoid circular references, and the problems of tracking down errors generated by the same. For example, if Class X imports Class Y, and Class Y imports class X, there’s a good chance you may run into a compile time error.

There are exceptions where you will need to import the relevant .h file(s) versus using @class – if in the example above we needed to reference a method in HomeBrewBook within HomeBrewBook.h, the compiler would need the specifics on the method call (its parameters, etc).

Best Practice

To keep things consistent in your code, I would recommend you use the @class directive to make a forward reference to classes within interface files (like the first code example) and only import the .h file if needed.

17 Comments

  1. Two minor additions: 1. You can actually use @protocol to
    make a forward protocol reference, and it works the same way. 2.
    The main reason not to use a class/protocol forward reference in
    your header is when you’re inheriting from it.

  2. Great article. This conversation came up between myself and
    someone learning objective-c a few days ago. This is much more
    straightforward than my ramblings.

  3. No, sorry I’m still confused. In what situation would you
    want to reference a class and then not use it?

  4. Doesn’t an #import prevent a circular reference by only importing the class if said class isn’t already imported?

    • Interesting consideration. Here is a line from the Apple Objective-C reference: “This directive (#import) is identical to #include, except that it makes sure that the same file is never included more than once.”

      I imagine that is referring to once for each file compiled, not across the entire build process.

      One consideration of using @class versus always #importing is the potential problem Apple describes in the same Obj-C book as above: “…it avoids potential problems that may come with importing files that import still other files. For example, if one class declares a statically typed instance variable of another class, and their two interface files import each other, neither class may compile correctly.”

  5. If you have to import a class to be able to use it, then it seems like using @class is only useful if you’re developing some code where you haven’t yet built a class, but want the current code to refer to it without producing errors?

    What would happen if I included an @class directive for a class that I never import or implement? Even though the complier won’t error, won’t my code fail to work when it tries to call methods from the non-existent class?

    • You have to import a class in the file that references the class, for example, in the implementation file (.m) where you create an instance of the class and reference instance variables or public methods.

      If you included the @class directive and never did an import in the .m where you attempted to use the class (e.g. create an instance), the compiler should generate an error as it needs more information about the class being referenced.

  6. If your code is designed in a way that you end up having a
    circular reference, it might as well make sense putting both class
    together as one single class

  7. With the new compiler (LLVM compiler 1.6,) that comes with XCode 3.2.5, the circular #import issue was automatically solved. The compiler now is smart enough to stop importing headers once the dependence was solved. So, @class became pratically useless. But it’s still nice to remember it’s old need.

    • As I mentioned above, from the Apple docs, it looks like there is still relevance to @class, even if circular references are not of concern:

      “…it avoids potential problems that may come with importing files that import still other files. For example, if one class declares a statically typed instance variable of another class, and their two interface files import each other, neither class may compile correctly.”

  8. I’ve been using the @class directive for a while now, but I still can’t figure out how to use the @protocol directive properly without getting a complier warning. For example: if I want my view controller to implement the I have to #import “ClassA.h”. I cannot just “@protocol ClassADelegate;” without getting a compiler warning. Any ideas?

    • Just Declare all your protocols in a header file, and import the header file for all .h of the class files where you are assigning protocol. To make it cleare, as below

      //My Protocol.h

      declare protocols

      //MyClassImplementingProtocol.h
      #import “My Protocol.h”

      @interface className:UIViewController {

      }

      Please let me know if it was not clear enough

  9. oh, oops. That was just a typo when I copied the code. The issue I am having is at this line:

    {
    XYpoint *origin; // this line is where the problem is
    }

    I keep getting the error message “expected identifier or “9” ”

    But I was following what the book said.

Comments are closed.