Olof_t
Olof_t

Reputation: 768

Generics: Incompatible pointer types initializing 'NSMutableArray<subclass *> *' with an expression of type 'NSMutableArray<__kindof superclass *> *

I have this property:

@property (nonatomic) NSMutableArray <__kindof superclass*> *items;

I populate it with objects for a subclass (which should be ok due to __kindof). I retrieve the array like this:

NSMutableArray <__kindof subclass*> *items = holderObject.items;

But now I get this compiler warning:

Incompatible pointer types initializing 'NSMutableArray<subclass *> *' with an expression of type 'NSMutableArray<__kindof superclass *> * _Nullable'

Isn't this what __kindof is suposed to do for us? What am I doing wrong here?

UPDATE 2: This only happens for mutable arrays.

Update: here is some simple code that show the warning:

Create a new class GenericsError.h:

@import UIKit;
@interface GenericsError : NSObject
@property (nonatomic) NSMutableArray <__kindof UIViewController*> *generics;
@end

In any viewController just add:

GenericsError *error = [GenericsError new];
NSMutableArray <ViewController*>* controllers = error.generics;

(substitute ViewController for whatever your viewController is called). I'm not adding or creating anything, just getting the compiler warning for the generics-error.

Upvotes: 3

Views: 698

Answers (2)

newacct
newacct

Reputation: 122449

The generic parameter of NSMutableArray is invariant (it's declared @interface NSMutableArray<ObjectType> and not @interface NSMutableArray<__covariant ObjectType> or @interface NSMutableArray<__contravariant ObjectType>). That means the type argument must match exactly for it to be compatible.

That means NSMutableArray<NSString *> * cannot be assigned to NSMutableArray<NSObject *> *, or vice versa, even though NSString * can be assigned to NSObject *. (On the other hand, the type parameter of NSArray is covariant (it is declared NSArray<__covariant ObjectType>), which means NSArray<NSString *> * can be assigned to NSArray<NSObject *> *.)

NSMutableArray<NSString *> * cannot even be assigned to NSMutableArray<id> *, or vice versa, even though NSString * can be assigned to id and id can be assigned to NSString *, in both directions. I guess the idea is that id turns off static type checking, but only on uses of the actual type id, and not for types that have id as a type argument.

__kindof superclass * is kind of a "limited" version of id -- it turns off static type checking, but only when assigning to and from subtypes of superclass *. For the same reason that NSMutableArray<id> * cannot be assigned to or from NSMutableArray<someclass *> * above, NSMutableArray<__kindof superclass *> * also cannot be assigned to or from NSMutableArray<subclass *> *.

Upvotes: 1

Daniele Pantaleone
Daniele Pantaleone

Reputation: 2733

You have an inheritance A -> B (where A is the subclass and B is the superclass), you declared a container to hold instances of A but you are trying to put instances of B inside the container. It's not guaranteed that all the instances of B are also instances of A.

Imagine to have another class C -> B (where C is a subclass of B, and B is the same superclass mentioned above). In this case C is __kindof B but it's not __kindof A, hence the warning.

Long story short: you need to use __kindof superclass when declaring your container, to suppress the warning.

Upvotes: -1

Related Questions