iOS __kindof NSArray?

Haa*_*nti 36 objective-c ios

我一直在检查iOS 9作为开发人员的新功能,其中一些像StackView看起来很棒.

当我去UIStackView的头文件时,我看到了这个:

@property(nonatomic,readonly,copy) NSArray<__kindof UIView *> *arrangedSubviews;
Run Code Online (Sandbox Code Playgroud)

什么是NSArray*的__kindof.我们现在能够在NSArray*上指定类型吗?

小测试:

@interface DXTEST ()
@property (strong, nonatomic) NSMutableArray <__kindof NSString *>  *strings;
@end

@implementation DXTEST

- ( instancetype ) init {
    self = [super init];

    if( self ) {
        _strings = [NSMutableArray new];

        [_strings addObject:@(1)]; <-- compiler warning wieeeeee
    }

    return self;
}

@end
Run Code Online (Sandbox Code Playgroud)

Art*_*mov 61

可悲的是,目前最高投票的答案有点不正确.

实际上,您可以将任何子类添加<T>到泛型集合中:

@interface CustomView : UIView @end
@implementation CustomView @end

NSMutableArray<UIView *> *views;
UIView *view = [UIView new];
CustomView *customView = [CustomView new];
[views addObject:view];
[views addObject:customView];//compiles and runs!
Run Code Online (Sandbox Code Playgroud)

但是,当您尝试检索对象时,它将被严格键入,<T>并且需要转换:

//Warning: incompatible pointer types initializing 
//`CustomView *` with an expression of type `UIView * _Nullable`
CustomView *retrivedView = views.firstObject;
Run Code Online (Sandbox Code Playgroud)

但是,如果您要添加__kindof关键字,则返回的类型将更改为,kindof T并且不需要转换:

NSMutableArray<__kindof UIView *> *views;
<...>
CustomView *retrivedView = views.firstObject;
Run Code Online (Sandbox Code Playgroud)

TLDR:Objective-C中的泛型可以接受子类<T>,__kindof关键字指定返回值也可以是子类<T>.


Mic*_*lum 41

我们现在能够在NSArray*上指定类型吗?

是的,通过Objective-C的新轻量级泛型.在您提供的示例中,您有一个type属性NSArray,它将接受UIViews的元素.

现在,这可以指定如下(没有__kindof).

@property(nonatomic,readonly,copy) NSArray<UIView *> *arrangedSubviews;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,数组将接受其类是的对象UIView,但不接受任何子类的对象UIView.该__kindof声明将数组的泛型类型标记为可以接受UIView类的实例和任何子类的实例的类型UIView.

编辑:

我删除了原始答案的大部分,因为我错误地认为指定数组的类型会阻止您插入不正确类型的对象,而事实并非如此.(感谢Artem Abramov指出这一点.请参阅下面的答案以获取更多详情)

Objective-C的泛型似乎存在,在访问泛型集合的元素时为您提供类型信息.例如,请考虑下面的代码添加UIViewUIImageViewNSMutableArray<UIView *>.两个对象都插入到数组中而没有编译器或运行时的任何抱怨,但是当您尝试访问元素时,如果变量的类型不是数组的泛型类型,则编译器会警告您(UIView),即使它是其中一个UIView子类.

NSMutableArray<UIView *> *subviews = [[NSMutableArray alloc] init];

[subviews addObject:[[UIView alloc] init]]; // Works
[subviews addObject:[[UIImageView alloc] init]]; // Also works

UIView *sameView = subviews[0]; // Works
UIImageView *sameImageView = subviews[1]; // Incompatible pointer types initializing 'UIImageView *' with an expression of type 'UIView *'

NSLog(@"%@", NSStringFromClass([sameView class])); // UIView
NSLog(@"%@", NSStringFromClass([sameImageView class])); // UIImageView
Run Code Online (Sandbox Code Playgroud)

现在,这会产生编译时警告,但不会在运行时崩溃.这个和数组的泛型类型被标记为的相同示例之间的关键区别在于__kindof,如果您尝试访问其元素的某个元素,编译器将不会抱怨,并将结果存储在类型为UIView 或者其类型之一的变量中子类.

NSMutableArray<__kindof UIView *> *subviews = [[NSMutableArray alloc] init];

[subviews addObject:[[UIView alloc] init]]; // Works
[subviews addObject:[[UIImageView alloc] init]]; // Also works

UIView *sameView = subviews[0]; // No problem
UIImageView *sameImageView = subviews[1]; // No complaints now!

NSLog(@"%@", NSStringFromClass([sameView class])); // UIView
NSLog(@"%@", NSStringFromClass([sameImageView class])); // UIImageView
Run Code Online (Sandbox Code Playgroud)

  • 此视频中讨论了轻量级泛型:https://developer.apple.com/videos/play/wwdc2015-401 __kindof 在此视频中讨论:https://developer.apple.com/videos/play/wwdc2015 -401/?时间=1752 (2认同)

And*_*rea 7

iOS9在ObjC上引入了轻量级泛型.ObjC是一种非常动态的语言,而SWIFT更喜欢静态类型,为了最大化互操作性和类型检查现在在ObjC中你可以声明一个这样的数组:

NSArray<UIView *> *views;
Run Code Online (Sandbox Code Playgroud)

这意味着所有视图对象都是UIView对象的实例,想象UIView子视图属性它可以包含可以是不同类型的元素,UIViews和继承自的对象UIView,但编译器会抱怨,因为即使它们继承它们也是不同的类型.
这是__kindof发挥作用.就像是说数组包含类型的对象UIView.
就像仍然使用一种id类型的优势,但仅限于一种类.