Circular Imports

Member
Posts: 61
Joined: 2009.01
Post: #1
I'm getting a problem with "circular" or two-way imports. That is, two separate classes that each use the other. If I eliminate all references from one class to the other, thereby making it a single-way import, then I've got no errors and everything runs fine. However, once I put the two-way importing back in, random unrelated compiler errors pop up.

Example:
Code:
//ClassA.h

#import "ClassB.h"

@interface ClassA : NSObject
{
     //blah blah
}
//blah blah
@end
Code:
//ClassB.h

#import "ClassA.h"

@interface ClassB : NSObject
{
     //blah blah
}
//blah blah
@end

That will create an error, whereas if I either don't import ClassA or ClassB, in one file or the other, all problems are solved.

It seems to me that there has to be a way around this. It's fairly ridiculous if you can't have a two-way link. Like what if your World contains an array of Entities, and the Entities need to know what World they're in? Similarly, what if you've got a circular import chain that goes through 5 or 6 classes? i.e. A imports B, B imports C, C imports D, D imports E, E imports A. Will that break it as well?

Any way to fix this?
Quote this message in a reply
Moderator
Posts: 3,571
Joined: 2003.06
Post: #2
In one of the headers (let's say B), instead of importing the other header (A) use:

@class ClassA;

[adding] You will probably need to #import "ClassA.h" in B's .m file though, which won't be a problem.
Quote this message in a reply
Member
Posts: 61
Joined: 2009.01
Post: #3
Works beautifully. Thanks very much.

After reading up on @class versus import, I've still got a question about that.

When should one or the other be used? Should I always use import unless I get a conflict?

I've read elsewhere that someone personally uses @class everywhere except in their .m calling an import on its own .h. Is this a good rule of thumb?
Quote this message in a reply
Moderator
Posts: 608
Joined: 2002.04
Post: #4
Best practice is @class everything you can, which is pretty much everything other than your superclass and any implemented protocols. #import in the implementation.

Says Apple: "The @class directive minimizes the amount of code seen by the compiler and linker, and is therefore the simplest way to give a forward declaration of a class name. Being simple, 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." (http://developer.apple.com/documentation...ion_3.html)
Quote this message in a reply
Moderator
Posts: 3,571
Joined: 2003.06
Post: #5
Yes, that's a good rule of thumb. I too use @class as much as possible in headers because it can greatly improve compilation times.
Quote this message in a reply
Member
Posts: 61
Joined: 2009.01
Post: #6
Thanks for all the pointers, everyone.

Another related question. It seems properties and functions aren't found correctly when I use an @class. I replace it with an #import, and everything is fine. I assume that this means that in the .m file I should always use #import and in the .h always use @class? Also when making a subclass I need to do #import or it doesn't work correctly either.
Quote this message in a reply
Moderator
Posts: 133
Joined: 2008.05
Post: #7
@class is a forward declaration. That means, you are telling the compiler that "This class exists somewhere, I'm not going to give you any details about it, other than it's okay to declare instance variables of that type."

So basically, if a class doesn't exist, and you try and declare an instance of it, the compiler throws up an error. Forward declaring a class just ignores those errors.

If you are using a class as a super class, you need to import it. Importing a header file essentially copies and pastes the contents of that file to wherever the import statement is. You need to know everything about a class if it is going to be a super class, because the subclass needs to declare those instance variables and methods from the super class.

So, the general rule is this: If your class has instance variables of some type, you should forward declare that type to shut the compiler up. If your class needs to know about anything else in a file (instance variables a class has, protocols declared in that file, methods that a class implements), it needs to import it. If you forward declare a class, you need to import it in your .m file.
Quote this message in a reply
Member
Posts: 61
Joined: 2009.01
Post: #8
All right, that's exactly what I discovered. Glad I'm doing the right thing.

Thanks very much for the help.
Quote this message in a reply
Member
Posts: 283
Joined: 2006.05
Post: #9
This is very helpful. I was about to ask the same question.
Quote this message in a reply
Post Reply