【问题标题】:Can I use __kindof with protocols in Objective-C?我可以在 Objective-C 中将 __kindof 与协议一起使用吗?
【发布时间】:2018-03-09 18:18:43
【问题描述】:

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

NSArray<__kindof BaseClass*> *myArray;

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

但是,我有BaseProtocol,而不是BaseClass,例如我所有有问题的课程都将符合BaseProtocol,无论它们的基类如何。我想使用轻量级泛型来规定“我的数组由符合BaseProtocol 的元素组成,但它们可以是任何类”。

例如在 C# 中,我可以说:List&lt;IMyInterface&gt;,这意味着列表由实现 IMyInterface 接口的元素组成(我知道 C# 具有强大的泛型,而 Objective-C 只有轻量级的泛型,而没有防止编译,但你明白了)。

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

例如我要写

NSArray<__kindof id<MyProtocol>> //compiles, but as the generic argument is "id", it accepts any object, including invalid ones

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?
}

【问题讨论】:

  • @Rob 对,我的错。固定的。 (关于第二条评论)
  • @Rob 我已经在一个新的干净项目中实现了一个示例,正如您所说,如果我尝试添加一个类型不符合协议的对象,我确实会收到警告。但即使我在我的主要项目中应用了确切的模式,我也没有收到警告。我没有玩过任何与警告相关的项目设置。可能是什么原因?
  • 如果你去“report navigator”,你可以拉起编译日志,它说这是-Wobjc-literal-conversion的结果。因此,返回第一个项目的构建设置并搜索“literal”,您将看到该设置(称为“Implicit Objective-C Literal Conversions”)。也许这是一个早于这些设置的旧项目。
  • @Rob 很有趣。该选项设置为“是”,在我的项目中,我尝试了NSArray&lt;SpecificClass&lt;MyProtocol&gt;*&gt;*,然后我做了NSObject *x = myArray[0];,如果我理解正确,现在应该会给我警告,但事实并非如此。
  • 在您上次的编辑中,您问为什么您没有收到关于您的新 AnotherClass *anotherInstance = myArray[1] 示例的警告。在该特定示例中我也没有看到该警告,但是当我将NSArray 声明更改为NSArray&lt;__kindof SpecificClass&lt;MyProtocol&gt;*&gt;*array; 而不是NSArray&lt;__kindof NSObject&lt;MyProtocol&gt;*&gt;*array;NSArray&lt;__kindof id MyProtocol&gt;*&gt;*array; 时会这样做。令我震惊的是,您的示例也应该产生警告。

标签: objective-c generics casting covariance


【解决方案1】:

这个语法是正确的:

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

【讨论】:

  • 好的,我想我对究竟是什么导致了错误有一些误解。如果我使用NSObject&lt;MyProtocol&gt; 而不是id&lt;MyProtocol&gt;,它会变得更加清晰。我仍然不知道为什么我在干净的项目中遇到了这个错误,但在我的主要项目中却没有。
猜你喜欢
  • 1970-01-01
  • 2018-10-30
  • 1970-01-01
  • 1970-01-01
  • 2012-01-07
  • 1970-01-01
  • 2012-12-24
  • 2023-03-25
  • 2019-01-25
相关资源
最近更新 更多