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。 在你提供的例子中,你有一个NSArray
types的属性, 它将接受 UIView
的元素。
现在,这可以被指定如下(没有__kindof
)。
@property(nonatomic,readonly,copy) NSArray<UIView *> *arrangedSubviews;
在这种情况下,数组将接受类为UIView
对象,但不接受任何UIView
子类的对象。 __kindof
声明将数组的generics标记为可接受UIView
类的实例和UIView
的任何子类的实例。
编辑:
我已经删除了原来的大部分答案,因为我错误地认为指定数组的types将阻止您插入不正确types的对象,事实并非如此。 (感谢阿蒂姆·阿布拉莫夫 ( Artem Abramov)指出了这一点,请参阅下面的答案以获取更多详细信息)
在访问generics集合的元素时,Objective-C的generics似乎存在以提供types信息。 例如,考虑以下将UIView
和UIImageView
到NSMutableArray<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
和从UIView
inheritance的对象,但编译器会抱怨,因为即使它们inheritance不同种类。
这是__kindof
发挥作用。 就像是说数组包含了一些types为UIView
对象。
就像仍然使用一个id
types的优点,但限于一种类。
@阿布拉莫夫是正确的。 我想指出额外的一点,正如在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]
到任何具有UIView
或UIView
本身的子类的variables。
重要说明:即使没有__kindof
,你也可以在NSArray<UIView *>
存储UIView
子类。 __kindof
只是关于访问数组的元素。
- 如何在Xcode 4中切换.h和.m
- 在appdelegate中设置初始viewcontroller – swift
- (NSFetchedResultsController):无法读取caching文件来更新存储信息时间戳
- UIAlertView addSubview在iOS7中
- 我如何正确检测在iOS中使用JavaScript和Phonegap的方向变化?
- 如何减less使用UIImagePickerController创build的video的文件大小?
- 在iOS中实现HTTP Live Streaming
- 最好的方法来parsingURLstring来获取键的值?
- 如何重置iPhone模拟器中的NSUserDefaults数据?