我可以在Objective-C中使用__kindof协议吗?

时间:2018-03-09 18:18:44

标签: objective-c generics casting covariance

我知道我可以使用__kindof关键字来使用Objective-C的轻量级泛型,例如

NSArray<__kindof BaseClass*> *myArray;

这将删除将数组中的任何对象分配给派生类的任何警告。

但是,我BaseClass代替BaseProtocol,而不是BaseProtocol。无论基类如何,我所讨论的所有类都将符合BaseProtocol。我想使用轻量级泛型来指示&#34;我的数组由符合List<IMyInterface>的元素组成,但它们可以是任何类&#34;。

例如在C#中,我可以说:IMyInterface这意味着该列表包含实现NSArray<__kindof id<MyProtocol>> //compiles, but as the generic argument is "id", it accepts any object, including invalid ones 接口的元素(我知道C#具有强大的泛型,而Objective-C只有轻量级泛型这并不妨碍编译,但你明白了。

有没有办法在Objective-C上实现这个功能?

E.g。我想写

NSArray<id<__kindof MyProtocol>> //doesn't compile

@protocol MyProtocol

@end

@interface MyClass : NSObject<MyProtocol>

@end

@implementation MyClass

@end

@interface AnotherClass : NSObject

@end

@implementation AnotherClass

@end



NSMutableArray<__kindof id<MyProtocol>> *myArray;

void test(){
    MyClass *myClassInstance = [[MyClass alloc] init];
    AnotherClass *anotherClassInstance = [[AnotherClass alloc] init];

    myArray = @[].mutableCopy;
    [myArray addObject:myClassInstance];
    [myArray addObject:anotherClassInstance]; //i get warning. good.

    MyClass *returnedInstance = myArray[0];
    AnotherClass *anotherInstance = myArray[1]; //why don't I get a warning here?
}

这可能吗?

更新

这是一个完整的自包含代码:

Composite Template

1 个答案:

答案 0 :(得分:4)

这种语法是正确的:

NSArray <__kindof id <MyProtocol>> *array = ...

您也可以省略__kindof,并且仍然享受轻量级的泛型。即使没有该关键字,它仍会警告您添加错误类型的对象。如果要从对象中拉出一个对象并将其分配给没有强制转换的子类型,则使用__kindof,否则不需要__kindof:

NSArray <id <MyProtocol>> *array = ...

如果将特定类型的对象添加到数组中,这两种模式都会发出警告,但该类型不符合MyProtocol

如果您尝试单独添加id类型的对象,那么这不会警告您。因此,请避免在代码中使用不合格的id类型,并且您将享受轻量级泛型。

如果您仍未看到警告,请确保已启用-Wobjc-literal-conversion警告。所以,回到第一个项目的构建设置并搜索“literal”,你会看到设置(称为“Implicit Objective-C Literal Conversions”)。

考虑这个例子:

@protocol MyProtocol
@end

@interface Foo: NSObject <MyProtocol>
@end

@interface Bar: Foo
@end

@interface Baz: NSObject
@end

然后考虑:

Foo *foo = [[Foo alloc] init];
Bar *bar = [[Bar alloc] init];
Baz *baz = [[Baz alloc] init];
id   qux = [[Baz alloc] init];

NSArray <id <MyProtocol>> *array1;
array1 = @[foo, bar, baz, qux];           // warning: object of type 'Baz *' is not compatible with array element type 'Foo *'

注意,这会警告我们baz,而不是qux。所以要小心使用id类型。

id <MyProtocol> object1 = array1[0];      // no warning, great

所以,这是使用协议作为轻量级通用,它按预期工作。

您添加__kindof的唯一原因是您要避免此警告:

Foo *foo1 = array1[0];                    // warning: initializing 'Foo *__strong' with an expression of incompatible type 'id<MyProtocol> _Nullable'

在这种情况下,您使用__kindof

NSArray <__kindof id <MyProtocol>> *array2;
array2 = @[foo, bar, baz];                // again, warning: object of type 'Baz *' is not compatible with array element type 'Foo *'

id <MyProtocol> object2 = array2[0];      // no warning, great

Foo *foo2 = array2[0];                    // no warning, great