我可以将 __kindof 与 Objective-C 中的协议一起使用吗?
Can I use __kindof with protocols in Objective-C?
我知道我可以使用 Objective-C 的轻量级泛型,使用 __kindof
关键字,例如
NSArray<__kindof BaseClass*> *myArray;
这将删除任何将数组中的任何对象分配给派生对象的警告 class。
但是,我没有 BaseClass
,而是 BaseProtocol
,例如我所讨论的所有 classes 都将符合 BaseProtocol
,而不管它们的基础 class。我想使用轻量级泛型来指示 "my array consists of elements conforming to BaseProtocol
, but they can be any class".
例如在 C# 中,我可以说:List<IMyInterface>
这意味着该列表由实现 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?
}
这个语法是正确的:
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
我知道我可以使用 Objective-C 的轻量级泛型,使用 __kindof
关键字,例如
NSArray<__kindof BaseClass*> *myArray;
这将删除任何将数组中的任何对象分配给派生对象的警告 class。
但是,我没有 BaseClass
,而是 BaseProtocol
,例如我所讨论的所有 classes 都将符合 BaseProtocol
,而不管它们的基础 class。我想使用轻量级泛型来指示 "my array consists of elements conforming to BaseProtocol
, but they can be any class".
例如在 C# 中,我可以说:List<IMyInterface>
这意味着该列表由实现 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?
}
这个语法是正确的:
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