Objective-C Protocol Forward Declarations

ObjectProperties.h

@protocol ObjectProperties <NSObject>

@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSDate *date;
@property (assign, nonatomic) int64_t index;

@end

ClassA.h

#import <Foundation/Foundation.h>

@protocol ObjectProperties;

@interface ClassA : NSObject <ObjectProperties>

- (void)specialSauce;

@end;

ManagedClassA.h

#import <CoreData/CoreData.h>

@protocol ObjectProperties;

@interface ManagedClassA : NSManagedObject <ObjectProperties>

- (void)doSomething;

@end;

From the code example above, I have defined a protocol in a .h file to be used with both Core Data objects as well as plain ol' vanilla objects. It seems like "noise" to have conforming classes ‪#import‬ the protocol in the their header; it would be cleaner to forward declare the protocol & import‬ in the implementation file as I've shown above. However, Xcode throws a warning when doing it this way:

Cannot find protocol definition for 'ObjectProperties'

The code compiles, and mostly works. I say mostly because there's some funkiness with Core Data trying to dynamically create the getters / setters for the scalar property, but I think that's probably because I've hit an edge case.

Of course, the most obvious work around is to just import the protocol header into the class headers.

If my understanding is correct (and my knowledge is very recently acquired, and so it's entirely possible that I'm wrong), if I import the protocol into my class headers and make a change to the protocol, then all subsequent files that import my classes will have to be recompiled.

What is the correct way to do solve this type of problem?


Solution 1:

You cannot forward declare a superclass or a protocol that it conforms to. In those cases, you must include the header. This is because (in the case of superclass) the superclass's instance variables and methods become part of your class; or (in the case of protocols) the protocol's methods become methods declared in your class, without needing to declare them explicitly. (i.e. Now other people who include your class's header will see that your class declares those methods, as if you declared them yourself.) The only way that that could be possible is if they were already defined in this scope, i.e. their headers are imported.

#import <SomeClass.h>
#import <SomeProtocol.h> // these two must be imported

@interface MyClass : SomeClass <SomeProtocol>
@end

Forward declarations are useful for things that just show up in the type of a variable (specifically, an object pointer variable). Object pointers are all the same size and there is no difference between different types of object pointers at runtime (the concept of type of object pointer is just a compile-time thing). So there is no real need to know exactly what's in the classes of those types. Thus they can be forward declared.

@class SomeClass;
@protocol SomeProtocol; // you can forward-declare these

@interface MyClass {
    SomeClass *var1;
    id<SomeProtocol> var2;
}
@end

Solution 2:

Yes you are correct that all of the files will need to be recompiled, but this is necessary. Files that import the class header need to know the methods associated with the protocol it implements. If you tuck that definition away in the .m file then it is only visible to one file (since .m files never get imported). It's not the same as forward declaring classes. If you forward declare a protocol, you must declare it somewhere that is visible in the same scope as the forward declaration. I can't think of any example where this doesn't occur in the same file.

In ARC this is an error, because ARC needs to know about the memory management of the declared methods (do they return +1 instances, internal pointers, etc?)

Solution 3:

By declaring your class in MyClass.h, you have to import all header files for its superclass as well as protocols it adopted in the MyClass.h file. Protocols in Objective-c is considered as a variant type of inheritance.

Forward declarations are usually used in class' member declaration .