#import in #import? [duplicate] - objective-c

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How can I have references between two classes in Objective-C?
Objective-C #import loop
I'm getting a couple errors in my code and I'm not sure but I think its because I'm #importing an interface inside another interface where I'm #importing the other interface. If I'm confusing you I'll give you an example.
#import "OneClass.h"
#interface SecondClass : NSObject
{
OneClass * obj;
}
#import "SecondClass.h"
#interface OneClass : NSObject
{
SecondClass * obj;
}

Yes, you have a circular import. The problem here is that the second import (the one that re-imports your first header) is basically ignored by the compiler, since it thinks it's already imported that header.
The solution here is to use #class forward-declarations instead of using #imports. Not only does this solver the circular import problem, but it's a better idea anyway since it breaks unnecessary dependency chains (e.g. if I edit OneClass.h, SecondClass.h won't need to be re-processed).
To apply this here, simply remove the #import OneClass.h in SecondClass.h and replace it with #class OneClass;
In the more general case, you don't ever need to #import a header file just to declare an ivar/property/method that uses a class from that header. The #class token is sufficient. You do, however, need to #import the header file if you're inheriting from the class, or if you're referencing another non-class type declared in that header. Also remember that if you use #class in your header, you need to remember to put the actual #import into your .m file.

If you're importing a header file you need to put the full file name. In this case...
#import "SecondClass.h" instead of #import "SecondClass"

You can declare the use of a class without having to #import its associated header, like so:
// #import "SecondClass.h" // no need for this anymore
#class SecondClass;
#interface OneClass : NSObject
{
SecondClass * obj; // OK
}

When there are no physical dependencies, you should be using forward declarations to minimise your build times:
// SecondClass.h
#class OneClass;
#interface SecondClass : NSObject
{
OneClass * obj;
}
#end
// OneClass.h
#class SecondClass;
#interface OneClass : NSObject
{
SecondClass * obj;
}
#end
It also happens to fix your dependency cycle ;)

Related

Swift class using Objective-C class using Swift class

I have an obj-c project to which I successfully added a new Swift class A, which is being used by some existing obj-c class B - the use of the automatically generated "MyProject-Swift.h" header worked as expected.
I also successfully added a new Swift class C that uses some existing obj-c class D - the use of the bridging header also worked as expected.
However, suppose I want to refer from my Swift class C to the existing obj-c class B (which in turn refers to the new Swift class A). In order to do that I need to import "B.h" to the bridging header. However, if I do that I get an error in class B: "'MyProject-Swift.h' file not found" (i.e., the file is no longer generated).
Am I doing something wrong or is this a kind of interaction between Swift and Objective-C that is not allowed? It looks like there is a kind of circular reference that the compiler is unable to solve.
--- EDIT ---
I'll try to make the question clearer by adding some code.
-- PREAMBLE --
I added a new Swift class to an obj-c project:
// SwiftClassA.swift
import Foundation
#objc class SwiftClassA : NSObject {
var myProperty = 0
}
The code compiles correctly and is translated into obj-c stubs in the automatically generated "MyProject-Swift.h" header like so:
// MyProject-Swift.h
...
SWIFT_CLASS("_TtC7MyProject11SwiftClassA")
#interface SwiftClassA : NSObject
#property (nonatomic) NSInteger myProperty;
- (instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
Now, one obj-c class uses SwiftClassA:
// ObjCClass.h
#import <Foundation/Foundation.h>
#import <MyProject-Swift.h>
#interface ObjCClass : NSObject
#property (nonatomic, strong) SwiftClassA *aProperty;
#property (nonatomic) int *aNumber;
#end
This also works seamlessly.
-- THE QUESTION --
Can I now create a new Swift class that refers to the obj-c class (ObjCClass) that is using the Swift class SwiftClassA?
This is what I can't do.
If I add the new Swift class:
// SwiftClassB.swift
import Foundation
#objc class SwiftClassB : NSObject {
var aPropertyOfClassB = 1
func someFunc() {
var objCObject = ObjCClass()
var theProperty = objCObject.aProperty
print("The property is \(theProperty)")
}
}
this of course won't compile because of "Use of unresolved identifier 'ObjCClass'". So I need to add that to the bridging header file:
// BridgingHeader.h
#ifndef MyProject_BridgingHeader_h
#define MyProject_BridgingHeader_h
...
#import "ObjCClass.h"
#endif
However, if I do that, the ObjCClass.h file won't compile giving a "'MyProject-Swift.h' file not found".
I've read in several places (with no example, though) that this may mean that there is a circular reference and that a forward reference using #class could solve the problem. However, I'm not sure what needs to be forward referenced and where, and all my attempts failed.
I hope the question is no longer confusing now!
This is a typical cyclical referencing problem.
Be careful to read the docs:
To avoid cyclical references, don’t import Swift into an Objective-C header file. Instead, you can forward declare a Swift class to use it in an Objective-C header. Note that you cannot subclass a Swift class in Objective-C.
So, you should use "forward declare" in .h, and #import in .m:
// ObjCClass.h
#import <Foundation/Foundation.h>
#class SwiftClassA;
#interface ObjCClass : NSObject
#property (nonatomic, strong) SwiftClassA *aProperty;
#property (nonatomic) int *aNumber;
#end
// ObjCClass.m
#import "ObjCClass.h"
#import "MyProject-Swift.h"
#implementation ObjCClass
// your code
#end

#import does not protect against circular calls?

I have two classes ClassA and Class B (they are viewControllers).
Class A is a delegate of classB.
ClassA "laucnhes" and instance of ClassB.
ClassB call methods on classA.
Let's say it's :
#import "ClassB.h"
#interface ClassA : NSObject {
ClassB* subController;
}
- (void) doThis;
-------------------------------
#import "ClassA.h"
#interface ClassB : NSObject {
ClassA* delegate;
}
-------------------------------
#implementation ClassB
- (void) someMethod {
AnObject* myObj = [self.delegate.arr objectAtIndex:8];
[self.delegate doThis];
}
Doing that way, A must import B, and B must import A.
If B do not import A (with just #class A), there is a compile error for the used attribute from A.
If B imports A, there is a compile error on the ClassA* delegateline.
Why do I have those compile errors? Doesn't #import protect again recursive calls ?
I don't need a solution to solve that problem, I know how I may do this.
But I wonder why my #import cause those problems. These are not #includes...
In .h files prefer #class to #import. Both can then be imported in the .m implementation files.
// ClassA.h -------------------------------
#class ClassB;
#interface ClassA : NSObject {
ClassB* subController;
}
- (void) doThis;
// ClassB.h -------------------------------
#class ClassA;
#interface ClassB : NSObject {
ClassA* delegate;
}
// ClassB.m -------------------------------
#import "ClassA.h"
#import "ClassB.h"
#implementation ClassB
- (void) someMethod {
AnObject* myObj = [self.delegate.arr objectAtIndex:8];
[self.delegate doThis];
}
Using #class statements instead of #import also reduces dependencies and makes the remaining ones more clear. It can also speed up compile times.
Why do I have those compile errors? Doesn't #import protect again recursive calls ?
#import protects against repeatedly importing the same header into the same module, whether by circular includes/imports or not. It protects against that by not letting you do it: Only the first #import of a header works; subsequent #imports of the same header are ignored.
In a circular #include situation, the preprocessor would go around the circle some number of times and then fail the build before you even get to compilation. Using #import prevents the preprocessor from getting wedged and lets the preprocessor succeed, but circular-#import code is still dodgy at best and usually will not compile.
So, on to your specific situation.
For the code you showed in your question, #class will work in either or both headers, and indeed you should use it in both. You'll also need to #import both headers in both .m files.
If B do not import A (with just #class A), there is a compile error for the used attribute from A.
If you mean “there is a compile error at each point where I use that attribute of type ClassA *”, then yes: You can't talk to that object because you haven't imported its interface, so the compiler doesn't know what messages you can send to a ClassA instance. That's why you need to import its interface.
If B imports A, there is a compile error on the ClassA* delegateline.
If both headers import each other, then you have this:
ClassA.m:
ClassA.h
ClassB.h
ClassA.h (ignored because this was already imported by ClassA.m)
ClassB.m:
ClassB.h
ClassA.h
ClassB.h (ignored because this was already imported by ClassB.m)
There is no way this can work without one interface preceding the other without the other interface preceding it. That's the circle you're running into—the circle that #import exists to break. #include allows the circle, and thereby gets wedged:
ClassA.m:
ClassA.h
ClassB.h
ClassA.h
ClassB.h
ClassA.h
ClassB.h
ClassA.h
ClassB.h
ClassA.h
ClassB.h
ClassA.h
ClassB.h
(fails at some point)
Hence #import.
So you cannot import each header from the other. Hence #class.
But you still need to import each header from each module. That is, in fact, exactly what you need to do: Use #class in each header and use #import (on both headers) in each module.
This compile complaint can be avoided by declaring
#class ClassB;
in the .h file. The ClassB.h can then be included into the .m file.
So you are right on that one. Contrary to urban myth, #imports work pretty much like #includes in the sense that the compiler has to check the file.
See this (duplicate?) question for your philosophical problem.
I think you'll find that #import only protects against multiple inclusion once it has already been successfully included once, so to speak.
ie, in your case, it hasn't successfully imported classa.h before it is asked to import it again, so it does so.

#import in objective C: Am I doing this wrong?

Sorry, couldn't find a more appropriate title.
In My code I have two classes which should know of each others existence. So I use an instance variable which points to the other class. For that to work (I guess?) the other classes headers file should be imported so it knows which methods it has and such.
Here is my code (stripped down)
MainMenuController.h:
#import <Cocoa/Cocoa.h>
#import "IRCConnection.h"
#interface MainMenuController : NSViewController {
IRCConnection *ircConnection;
}
#property (strong) IRCConnection *ircConnection;
#end
IRCConnection.h:
#import <Foundation/Foundation.h>
#import "MainMenuController.h"
#interface IRCConnection : NSObject {
MainMenuController *mainMenuController;
}
#property (strong) MainMenuController *mainMenuController;
#end
As you can see they both import each other, but this creates an error (Unknown type name 'IRCConnection') in one, and in the other Unknown type name 'MainMenuController'.
However when the connection is just one way (e.g. only MainMenuController knows about IRCConnection) and thus there is only an import statement in one of the two, it works fine.
How can I have them to know about each other? In both ways.
Hope this question makes any sense.
you could remove the import from IRCConnection.h and use a #class statement instead.
like this:
#import <Foundation/Foundation.h>
#class MainMenuController;
#interface IRCConnection : NSObject {
then add a #import "MainMenuController.h" to IRCConnection.m
In the header, use forward declaration:
#class IRCConnection;
#interface MainMenuController : NSViewController {
IRCConnection *ircConnection; // ok
}
In the source file (.m), do #import.
You cannot have circular imports. You need to break them up, or introduce some forward declarations.

Cannot find protocol declaration

I have two view controllers A and B, and they both have each other as their delegates.
When I did nothing except define the protocols at the beginning of the header files and #import the other's header file, I got two errors along the lines of -
cannot find protocol declaration for "BDelegate", which was showing in
A.h (where I wrote ) cannot find protocol declaration for
"ADelegate", which was showing in B.h (where I wrote )
Looking online, people had written earlier that the circular inclusion of header files could be leading to the problems. They recommended either using #include instead, or #class declaration like -
#class A
instead of
#import A.h
inside #import B.h
I have tried almost every combination of these imports, and #classes, and #include but still can't get rid of the warnings. Also, solutions online recommended moving the #import to the .m files but that didn't help either. Part of the reason is that the solutions online are kinda fuzzy - if you could break it down that would be great.
Any suggestions about what can be done to fix this?
-- BigViewController.h --
#import "BaseViewController.h"
#include "BaseViewController.h"
#class BigViewController;
#protocol BigViewControllerDelegate
-(void) BigViewController:(BigViewController *) bigView;
#end
#interface BigViewController : UIViewController <BaseViewControllerDelegate>
{
//delegate
id <BigViewControllerDelegate> delegate;
ivars...
}
#properties...
#end
--------------------------------------------------
-- BaseViewController.h --
#<UIKit/UIKit.h>
#import "BigViewController.h"
#include "BigViewController.h"
#class BigViewController;
#protocol BaseViewControllerDelegate
- (void) setParametersWithItemChosen:(Item *) item;
#end
#interface BaseViewController : UIViewController <...BigViewControllerDelegate...>
{
ivars...
//delegate
id <BaseViewControllerDelegate> delegate;
}
#properties...
#end
Let me reduce the sample even further, and label the lines:
VC1.h
#import "VC2.h" // A
#class VC1;
#protocol VC1Delegate // B
#end
#interface VC1 : UIViewController <VC2Delegate> // C
#end
VC2.h
#import "VC1.h" // D
#class VC2;
#protocol VC2Delegate // E
#end
#interface VC2 : UIViewController <VC1Delegate> // F
#end
Consider what happens when something #imports VC1.h: It reaches line A, then the import is processed. Line D does nothing because VC1.h was already imported. Then line E is processed. Then line F, and we get an error because we haven't gotten to line B yet so the protocol is not declared!
Consider then what happens when something #imports VC2.h: It reaches line D, then the import is processed. Line A does nothing because VC2.h was already imported. Then line B is processed. Then line C, and we get an error because we haven't gotten to line E yet so the protocol is not declared!
The first step is to reconsider whether both of these classes really need to be each other's delegates. If you can break the cycle, that would probably be the way to go. If not, you'll need to restructure your headers. The most straightforward way is probably to put the delegates into their own headers:
VC1Delegate.h
#class VC1;
#protocol VC1Delegate // B
#end
VC1.h
#import "VC1Delegate.h"
#import "VC2Delegate.h"
#interface VC1 : UIViewController <VC2Delegate> // C
#end
VC2Delegate.h
#class VC2;
#protocol VC2Delegate // E
#end
VC2.h
#import "VC1Delegate.h"
#import "VC2Delegate.h"
#interface VC2 : UIViewController <VC1Delegate> // F
#end
If you trace through the imports now, you'll see that the appropriate protocols will now always be declared before the #interface lines try to use them.
Write protocol declaration code above the #import lines
e.g.
#protocol -----
#end
import ----
#interface classname ---
I had almost the same problem, and I fixed it thanks to the answer above, but in a slightly different way.
all I did was to put the #import line after the protocol declaration in the header file.
hope I can help. and if anyone know that this is bad programing for some reason, pls let me know
I found another solution to this issue because I didn't really like the idea of just having some #imports in between the class and the protocol declaration.
Basically you just move <YourProtocolName> from the .h class file to the class extension in the .m file
So in your .m file you add
#interface YourClassName () <YourProtocolName>
#end
I don't know if this is really a good practise but it looks like a cleaner solution to avoid the import cycles.
Try putting the < BaseViewControllerDelegate > or < BigViewControllerDelegate > in implementation file rather then header file. It will work.
I followed the fix of moving the protocol before the import and it fixed the problem... the import included the delegate, so that was causing the problem.
But then I thought, why was I importing the delegate anyway? I wasn't referencing its properties and I wasn't calling any of its methods directly (that's what the protocol declare was for).
I tried commenting out the import of the delegate and saw where the error came up and found that what I was importing when I was importing the delegate was actually a declaration that the delegate was importing i.e. I was importing A (also my delegate), A was importing B, what I was actually using was B. So I left the import of A commented out and added an import for B. Then I could put the import-protocol order back the way it was.

Cocoa: Build Warning that s Forward Declared Class's and #interface May not Exist

I am trying to build the Clustering Plug in my project under Leopard. I have following two questions.
In the project an interface class is defined as
#interface ClusteringController : NSWindowController
{
.......
.....
....
}
#end.
And this class is used in implementation class using forward declaration:
#class ClusteringController;
then in one function it is used as:
- (long) filterImage:(NSString*) menuName
{
ClusteringController *cluster = [[ClusteringController alloc] init];
[cluster showWindow:self];
return 0;
}
When I build this project it produces the warning:
warning: receiver 'ClusteringController' is a forward class and corresponding #interface may not exist
Also there is one more warning produced:
warning: no '-updateProxyWhenReconnect' method found
This warning is coming for the following line of code:
if(delegate) [delegate updateProxyWhenReconnect];
Can anybody help me to overcome these warnings?
A forward declaration is used when the header file will be imported after the interface. It looks to me that you've used the #class directive after the interface for the class itself.
The normal use of a forward class declaration looks like this:
#import "SomeSuperClass.h"
#class Forwardclass;
#interface SomeClass : SomeSuperClass
{
Forwardclass anIvar;
}
#property Forwardclass anIvar;
#end
#import "SomeClass.h"
#import "ForwardClass.h"
#implementation SomeClass
#synthesize anIvar;
-(void) setAnIvar:(ForwardClass *) aForwardClass;
#end
The #class directive is never used in an implementation (.m) file.
That's not what #class is for.
You use #class in the header file for another class, to tell the compiler that the class you're declaring does exist. Without it, the compiler would not know that that's a class name, and when you declare a variable as holding a pointer to an instance of that class, the compiler would think that you're just making up words. Using #class is called forward-declaring the class.
Your situation is different. You're in the implementation file for that class.
What the compiler needs from you now is the class's #interface. The warning is telling you that the compiler needs an #interface, but you haven't given it one (so, as far it knows, the #interface “may not exist”).
Normally, you would have written the #interface in a header file; how now to get it into the implementation file?
That's where the preprocessor comes in, with its #import directive. At the top of the implementation file (ClusteringController.m), import the header file:
#import "ClusteringController.h"
The preprocessor will replace this with the contents of that file, then hand the preprocessed code to the compiler, which will see the #interface there.