3
votes

I have a project structure in which:

ClassA has a strong reference to ClassB

ClassAA subclasses ClassA and has a strong reference to ClassBB

ClassB has a weak reference to ClassA (named state and defined using @class ClassA;)

ClassBB subclasses ClassB and overloads state to be ClassAA and uses @class ClassAA; to do so

Because of the overloaded state in ClassBB, the compiler can't tell that ClassAA is a subclass of ClassA. Thus, I get this warning:

Property type 'ClassAA *' is incompatible with type 'ClassA *' inherited from 'ClassB'`

How can I tell the compilter that I know what I'm doing, so the warning hides itself? Thanks for any advice. Below is the project structure that creates this warning:

ClassA.h

#import <Foundation/Foundation.h>
#import "ClassB.h"

@interface ClassA : NSObject

@property (strong)
ClassB* view;

@end

ClassB.h

#import <UIKit/UIKit.h>
@class ClassA;

@interface ClassB : UIView

@property (weak)
ClassA* state;

@end

ClassAA.h (inherits from ClassA)

#import "ClassA.h"
#import "ClassBB.h"

@interface ClassAA : ClassA

@property (strong)
ClassBB* view;

@end

ClassBB.h (inherits from ClassB)

#import "ClassB.h"
@class ClassAA;

@interface ClassBB : ClassB

@property (weak)
ClassAA* state; <-- Where the warning is occurring

@end
2
I'm just wondering-- Did you try with #import "ClassAA.H" rather than the @class ClassAA forward declaration? I also wonder whether the property needs to be in the public @interface, in which case you could move it to the .m and get the same effect.stevesliva
The only reason I'm using @class is because circular dependencies are not allowed. And I would prefer to keep it in the public @interface, sorry.aleclarson

2 Answers

1
votes

This is certainly a Quality-of-Implementation issue in the Clang Objective-C frontend; there's nothing wrong with your code, so the only question is how to trick the compiler into shutting up for a moment.

If you change your "ClassBB.h" to look like this, then it will compile without the warning — and [[ClassBB new] state] will still have the correct static type (namely ClassAA*, not ClassA*).

#import "ClassB.h"              // #imports required by is-a relationships

@interface ClassBB : ClassB @end          // define the is-a relationships

#import "ClassAA.h"            // #imports required by has-a relationships

@interface ClassBB()                     // define the has-a relationships
@property (weak) ClassAA* state;
@end

I don't think anyone would call this class layout a "best practice", but at least it's a clean-ish way to write this sort of not-really-circular dependency. (Notice the utter lack of @class declarations. IMO, @class is a code smell. This header file isn't responsible for defining ClassAA, so it should leave it up to "ClassAA.h" whether ClassAA is an Objective-C class, a typedef, a macro, or something else.)

1
votes

This is probably a good place to use a Protocol. Define a CassAConforming Protocol and a ClassBConforming Protocol. Make the properties id And id in your base classes. Then make your subclasses conform to those protocols. This gives you the type checking Xcode wants to do for you and allows you to be flexible in the implementation.