2

In DataProvider.h

@protocol NewDataProviderProtocol 

- (void)fetchNewData;

@end

In SomeClass

#import DataProvider.h
@interface SomeClass :NSObject <NewDataProviderProtocol>

@end

When I try to make SomeClass conform to NewDataProviderProtocol it says,

No type or protocol named 'NewDataProviderProtocol'

It's weird since I already imported header DataProvider.h in which the protocol is declared.

So I forward declare the NewDataProviderProtocol before interface of SomeClass but xcode warns

Cannot find definition for **NewDataProviderProtocol**

What's the reason and the workaround for this?

Prajeet Shrestha
  • 7,978
  • 3
  • 34
  • 63
  • 1
    Do you import SomeClass.h into DataProvider.h, too? It sounds like a include-cycle which leads to one undeclared identifier. – Amin Negm-Awad May 06 '16 at 07:58

2 Answers2

1

A. Reason

Likely you have a include cycle, because you import SomeClass.h into DataProvider.h, too. This leads to an undeclared identifier.

Why is it that way? Let's have an example:

// Foo.h
#import "Bar.h"
@interface Foo : NSObject 
…// Do something with Bar 
@end


// Bar.h
#import "Foo.h"
@interface Bar : NSObject 
…// Do something with Foo 
@end

If you compile, let's say Foo.h, the precompiler will expand this:

He gets …:

// Foo.h
#import "Bar.h"
@interface Foo : NSObject 
…// Do something with Bar 
@end

… imports Bar.h (And strips comments … But let's focus on the main topic.) …

// Foo.h
   // Bar.h
   #import "Foo.h"
   @interface Bar : NSObject 
   …// Do something with Foo 
   @end

@interface Foo : NSObject 
…// Do something with Bar 
@end

Foo.h will not be imported again, because it is already imported. Finally:

   // Bar.h
   @interface Bar : NSObject 
   …// Do something with Foo 
   @end

@interface Foo : NSObject 
…// Do something with Bar 
@end

This is quite clear: If A relies on B and B relies on A, it is impossible for a serial data stream as a file is, to have A ahead of B and B ahead of A at the same time. (Files are not subjects of the theory of relativity.)

B. Solution

In most cases you should give your code a hierarchy. (For many reasons. Having no import problems is one of the least important.) I. e. in your code an import of SomeClass.h into DataProvider.h looks strange.

Having such a problem is a code smell. Try to isolate and repair the reason for that. Do not move code pieces into different location to find a pace, where it works. This is code lottery.

C. Structure

Usually you have a class that expects others to conform to a protocol. Let's have an example:

// We declare the protocol here, because the class below expects from other classes to conform to the protocol.

@protocol DataSoure
…
@end

@interface Aggregator : NSObject 
- (void)addDataSource:(id<DataSource>)dataSource 
// We are using a protocol, because we do not want to restrict data sources to be subclass of a specific class.
// Therefore in this .h there cannot be an import of that – likely unknown - class
@end

SomeClass, that conforms to the protocol

#import "Aggregator.h"

@interface SomeClass:NSObject<DataSource>
…
@end
Amin Negm-Awad
  • 16,582
  • 3
  • 35
  • 50
  • Thanx a ton. Now it's clear. That was the reason. :) – Prajeet Shrestha May 06 '16 at 08:16
  • 1
    Especially when factoring code out, it has happened 10923801239 times to me, to forget to remove an import … – Amin Negm-Awad May 06 '16 at 08:19
  • Yea that happend to me cuz I used concrete class previously and tried to seperate it into an abstraction. – Prajeet Shrestha May 06 '16 at 08:22
  • Now this raises another question. Where do we declare the protocols? In the class being served - SomeClass.h or class that serves the DataProvider.h ------or separate it out altogether in new file? Whats the best practice? – Prajeet Shrestha May 06 '16 at 08:26
  • Usually you in the class that expects others to conform to the protocol. This is, because this class can never rely on other – potentially unknown – classes. I'll add code to my A. – Amin Negm-Awad May 06 '16 at 08:27
  • Now I think It's better to separate it out in different .h file like DataProviderProtocol.h. If we do so if we need to make new concrete class that has different implementation of fetchData like LocalDataProvider.h It would make more sense if we import protocol declaration from DataProviderProtocol.h rather than DataProvider.h Dont you think? The client and server both needs abstraction. – Prajeet Shrestha May 06 '16 at 08:37
  • The comment above was before you put the C. Segment in your answer. That makes sense too. @Amin. Thanks for helping me out with this :) – Prajeet Shrestha May 06 '16 at 08:44
  • 1
    You can put it in a separate .h, too. Esp. this makes sense, if you have a bundle of classes expecting protocol conformance. (DataAggregator, DataSelector, DataCompressor …) – Amin Negm-Awad May 06 '16 at 08:49
0

Two things to change:

Change definition like this:

@protocol NewDataProviderProtocol <NSObject>    
- (void)fetchNewData;    
@end

Why? Why tack a protocol of NSObject to a protocol implementation

Import DataProvider in SomeClass.m only. You can always create an extension of SomeClass inside implementation file where you can bind the protocol to conform a particular class.

@interface SomeClass ()<NewDataProviderProtocol>

@end

Why? It's the best practice. And to overcome forward class declaration errors. E.g. Objective-C: Forward Class Declaration

Community
  • 1
  • 1
Hemang
  • 26,840
  • 19
  • 119
  • 186
  • Shifting protocol conformance to .m seem to work. But I can't figure why? And why should a protocol conform to too? Also I always conform the UITableViewDataSource in .h files why is not possible for custom protocol? – Prajeet Shrestha May 06 '16 at 06:21
  • @PrajeetShrestha Please review http://stackoverflow.com/questions/679822/why-tack-a-protocol-of-nsobject-to-a-protocol-implementation for why your protocol should extend the `NSObject` protocol. – rmaddy May 06 '16 at 06:24
  • @PrajeetShrestha And moving to the .m file worked for you because your .m file is probably importing the `DataProvider.h` file. – rmaddy May 06 '16 at 06:25
  • But since I imported DataProvider.h in SomeClass.h before declaring the interface of SomeClass shouldn't it work there as well? – Prajeet Shrestha May 06 '16 at 06:33
  • It will work normally. But I am pointing to future issues (?), its not necessary but its a good practice :) – Hemang May 06 '16 at 06:51
  • It is not a good practice, because what you have in the .h and the .m is a question of what you want to have public resp. privat. It is not a question of what can work-around bad code structure. – Amin Negm-Awad May 06 '16 at 08:10