iOS __kindof NSArray?

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

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

@property(nonatomic,readonly,copy) NSArray<__kindof UIView *> *arrangedSubviews; 

什么是NSArray * __kindof。 我们现在能够在NSArray *上指定一个types吗?

小testing:

 @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 

我们现在能够在NSArray *上指定一个types吗?

是的,通过Objective-C的新的轻量级generics。 在你提供的例子中,你有一个NSArraytypes的属性, 它将接受UIView的元素。

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

 @property(nonatomic,readonly,copy) NSArray<UIView *> *arrangedSubviews; 

在这种情况下,数组将接受类为UIView对象,但不接受任何UIView子类的对象。 __kindof声明将数组的generics标记为可接受UIView类的实例和UIView的任何子类的实例。

编辑:

我已经删除了原来的大部分答案,因为我错误地认为指定数组的types将阻止您插入不正确types的对象,事实并非如此。 (感谢阿蒂姆·阿布拉莫夫 ( Artem Abramov)指出了这一点,请参阅下面的答案以获取更多详细信息)

在访问generics集合的元素时,Objective-C的generics似乎存在以提供types信息。 例如,考虑以下将UIViewUIImageViewNSMutableArray<UIView *> 。 两个对象都被插入到数组中,而没有任何编译器或运行时的抱怨,但是当你尝试访问这些元素时,如果你的variables的types不是数组的genericstypes, 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 

现在,这会产生编译时警告,但不会在运行时崩溃。 这个和数组的genericstypes被标记为__kindof的同一个例子的主要区别在于,如果你试图访问它的一些元素,那么编译器不会抱怨,并且将结果存储在一个types为UIView的variables或者一个的子类。

 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 

可悲的是,目前排名最高的答案有点不正确。

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

 @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! 

但是当你试图检索这个对象的时候,它会被严格地键入为<T>并且需要强制转换:

 //Warning: incompatible pointer types initializing //`CustomView *` with an expression of type `UIView * _Nullable` CustomView *retrivedView = views.firstObject; 

但是,如果您添加__kindof关键字,返回的types将更改为__kindof kindof T ,不需要投射:

 NSMutableArray<__kindof UIView *> *views; <...> CustomView *retrivedView = views.firstObject; 

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

iOS9在ObjC上引入了轻量级的generics。 ObjC是一个真正dynamic的语言,而SWIFT更喜欢静态types,为了最大限度地提高互操作性和ObjC中的types检查,您可以声明如下的数组:

 NSArray<UIView *> *views; 

这意味着所有的视图对象都是UIView对象的实例,想象一下UIView子视图属性它可以包含不同types的元素, UIView和从UIViewinheritance的对象,但编译器会抱怨,因为即使它们inheritance不同种类。
这是__kindof发挥作用。 就像是说数组包含了一些types为UIView对象。
就像仍然使用一个idtypes的优点,但限于一种类。

@阿布拉莫夫是正确的。 我想指出额外的一点,正如在Objective-Cgenerics中提到的那样

__kindof说这个东西必须是X或更派生的类。 为什么这是必要的? 文档不会调用这个,但我怀疑它是因为在Objective-C中添加generics引入了一个巨大的难题:如果UIView.subviews现在是NSArray<UIView *>那么根据编译器的types检查器,现在很多代码是无效的: [view.subviews[0] setImage:nil]是假的,因为UIView没有图像属性。 我们已经被Objective-C编译器让你发送任何可见的消息select器到id的事实宠坏了…只有现在subviews数组不返回id,它返回UIView * 。 哎呀。

__kindof通过说__kindof数组不是显式的UIView *的数组,而是一个UIView *子类的数组来解决这个问题。 然后,编译器会让你发送任何可以应用到UIView *或其所有子类的select器,或者从该数组中分配一个元素到UIImageView * ,但是如果你尝试这样做,它仍然会抱怨: NSNumber *n = view.subviews[0]

这是在Xcode 7中引入的Clang的一个特性。我不认为它与iOS版本有关

如果你有一个返回__kindof SomeType *的函数,那么你可以将该函数的结果赋给一个types为SubtypeOfSomeType *的variables,而不需要显式的SubtypeOfSomeType * 。 没有__kindof编译器会抱怨不兼容的types。

其实在UIKit中至less有一个这样的函数: https : //developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/index.html#//apple_ref/occ/instm/UITableView/dequeueReusableCellWithIdentifier :

现在让我们把这个知识扩展到NSArray 。 在genericstypes参数中指定__kindof添加了__kindof来返回-objectAtIndex:-objectAtIndexedSubscript:types-objectAtIndex:实际上,返回ObjectType的genericsNSArray的任何方法),现在你可以很容易地分配调用stackView.arrangedSubviews[i]到任何具有UIViewUIView本身的子类的variables。

重要说明:即使没有__kindof ,你也可以在NSArray<UIView *>存储UIView子类。 __kindof只是关于访问数组的元素。