Class A defines the property "items" as an ordered set containing instances of class X.
Class B subclasses A, and redefines the "items" property as an ordered set containing instances of class Y, a subclass of X.
// Defined in class A
@property NSOrderedSet<__kindof X *> *items;
// Defined in class B
@property NSOrderedSet<__kindof Y *> *items;
All Y's are a kind of X's so the subclassed property should be valid, however Xcode generates the following warning within class B:
Property type 'NSOrderedSet<__kindof Y *> *' is incompatible with type 'NSOrderedSet<__kindof X *> *' inherited from 'A'
Am I actually doing something wrong or is this an Xcode bug? How can I remove this warning?
You cannot say that this is a bug, because it is a subject of discussions whether such a subclassing of parameters in a subclass is correct or wrong. Applying Liskov's substitution principle it has to be possible to add an item of X
to the set, because the base class allows this.Then it is an infringement of the principle, if the subclass forbids it.
However, you should remember that the "typing" of collections was introduced with Swift and was motivated to give the Swift compiler more type information. It likes that.
It is akin of anti-conceptual in pure Objective-C. Simply omit it, if you do not want to use the class from Swift.
To give you an example that is more simple. Having:
@interface BaseValue : NSObject
…
@end
@interface Subvalue : BasValue
- (void)anotherMethod;
…
@end
@interface BaseHolder : NSObject
@property NSMutableSet<BaseValue*>* values;
@end
@interface Subholder : BaseHolder
@property NSMutableSet<Subvalue*>* values;
@end
In this example, the members of the set are restricted to be instances of Subvalue
. Therefore the code inside Subholder
– and any code explicitly dealing with instances of Subholder
can expect, that the items inside the set can receive a message with the selector anotherMethod
.
But if you do this
BaseValue *baseValue = [BaseValue new];
BaseHolder *baseHolder = [Subholder new]; // Get an instance of *Subholder*. The assignment is allowed by Liskov … As long as Subholder introduces no additional constraints.
[baseHolder.values addObject:baseValue]; // From the compiler's point of view legal, because you said nothing about a subclass
This breaks, because at runtime it is an instance of Subholder
, but it has a member in its collection that does not match the constraint.