64

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
casillas
  • 16,351
  • 19
  • 115
  • 215
Ak1
  • 661
  • 1
  • 6
  • 6

6 Answers6

106

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.

Anomie
  • 92,546
  • 13
  • 126
  • 145
  • 6
    Thanks for this excellent explanation! For anyone wanting some clearly written examples of how this relates to Storyboards, SplitViews etc... Tim Roadley has some excellent tutorials at www.timroadley.com (I think that's the right address...). I was directed here from those pages. – Bertie Apr 13 '12 at 10:53
  • 1
    I was going to +1 this, but since it has 42 upvotes, and 42 is the secret of the universe, and this is the secret of the universe, I'll just leave this here. – CommaToast Oct 17 '13 at 16:12
  • Great i've been looking for this kind of answer for a while! – Simon Unsworth Feb 13 '18 at 10:17
103

Write protocol declaration code above the #import lines e.g.

@protocol -----

@end

import ----

@interface classname ---

Community
  • 1
  • 1
Vinner
  • 1,231
  • 1
  • 15
  • 16
  • 3
    thanx...it worked for me..but i have no idea how. Can you please explain this. – Surender Rathore May 19 '13 at 13:30
  • 1
    After 3 hours, you saved my life!! Can you explain it please? Thank you – Mário Carvalho May 22 '13 at 13:16
  • 3
    If time permits, read the answer by Anomie to get a further explanation of what could be occurring. Placing the protocol above the import ensure's that the protocol will be defined before imports are evaluated. This issue is similar to circular class dependencies. Anomie's suggestion to separate your delegates into their own files is highly recommended. – avelis Aug 07 '13 at 20:44
  • 1
    Check your included files. This is symptom of two mutually included headers. Sometimes it cause this error. – skywinder Oct 06 '13 at 18:08
  • Thanks a lot. This was annoying the heck out of me, but forces you to declare Protocols upfront I gather from what you guys are saying. The header makes more sense when you look at it though, since you split it in two, protocols and where they come from neatly at the top. Thanks much, this did it for me and I will make this a common practice from now on. – FrostyL Oct 29 '13 at 05:57
  • I confirm @skywinder thesis, it happens when there is a mutual inclusion between the headers. – Claus Feb 02 '15 at 17:32
  • what's up with the weird formatting of this post? what is it supposed to help communicate with all the different heading levels? – starball Feb 16 '23 at 23:20
37

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

litov
  • 540
  • 5
  • 16
  • 2
    After reading the other answers, I gave this idea a shot. It seems that it creates a looped import when two headers import each other. In the header with a protocol, moving the import of a header that calls the protocol below the protocol declaration was a perfect fix. – Abandoned Cart Aug 14 '12 at 02:44
12

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.

10

Try putting the < BaseViewControllerDelegate > or < BigViewControllerDelegate > in implementation file rather then header file. It will work.

coreDeviOS
  • 1,468
  • 2
  • 14
  • 27
  • 1
    THIS. No other answer in any other thread around StackOverflow fixed my situation except that one. Thanks. – Isaac May 31 '16 at 15:20
3

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.

dawid
  • 374
  • 5
  • 9