iOS __kindof NSArray?
Posted
技术标签:
【中文标题】iOS __kindof NSArray?【英文标题】:iOS __kindof NSArray? 【发布时间】:2015-07-14 06:26:41 【问题描述】:作为开发人员,我一直在查看 ios 9 的新功能,其中一些功能(例如 StackView)看起来很棒。
当我查看 UIStackView 的头文件时,我看到了这个:
@property(nonatomic,readonly,copy) NSArray<__kindof UIView *> *arrangedSubviews;
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
【问题讨论】:
您尝试添加错误的类型? 这就是美丽!在此之前可以正常工作,但现在使用 __kindof 它显示我添加了错误的类型。但是我找不到任何文档,所以我不知道发生了什么 不;那是泛型的使用,而不是__kindof
。
在 Xcode 7 中,Objective-C 支持泛型,允许您为 NSArray、NSSet 和 NSDictionary 等集合类指定类型信息。 developer.apple.com/library/prerelease/ios/documentation/…
@***foe 我明白了!在我当前的代码库中,我非常喜欢这个添加
【参考方案1】:
遗憾的是,当前投票最多的答案有点不正确。
您实际上可以将<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!
但是当您尝试检索对象时,它将被严格输入为 <T>
并且需要强制转换:
//Warning: incompatible pointer types initializing
//`CustomView *` with an expression of type `UIView * _Nullable`
CustomView *retrivedView = views.firstObject;
但如果您添加 __kindof
关键字,则返回的类型将更改为 kindof T
并且不需要强制转换:
NSMutableArray<__kindof UIView *> *views;
<...>
CustomView *retrivedView = views.firstObject;
TLDR:Objective-C 中的泛型可以接受<T>
的子类,__kindof
关键字指定返回值也可以是<T>
的子类。
【讨论】:
是的,就是这样,它避免了强制转换。 “遗憾的是,当前投票最多的答案有点不正确” 嘿,这是现在投票最多的答案。 那么现在票数最高的答案有点不正确,说明它本身是不正确的:)【参考方案2】:我们现在可以在 NSArray * 上指定类型了吗?
是的,通过 Objective-C 的新轻量级泛型。在您提供的示例中,您有一个 NSArray
类型的属性,将接受 UIView
s 的元素。
现在,可以按如下方式指定(不带__kindof
)。
@property(nonatomic,readonly,copy) NSArray<UIView *> *arrangedSubviews;
在这种情况下,数组将接受类为UIView
的对象,但不接受属于UIView
子类的任何对象。 __kindof
声明将数组的泛型类型标记为可以接受UIView
类的实例和UIView
的任何子类的实例。
编辑:
我已经删除了大部分原始答案,因为我错误地认为指定数组的类型会阻止您插入不正确类型的对象,但事实并非如此。 (感谢Artem Abramov 指出这一点。请在下方see his answer 了解更多详情)
Objective-C 的泛型似乎可以在访问泛型集合的元素时为您提供类型信息。例如,考虑以下代码,它将UIView
和UIImageView
添加到NSMutableArray<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
现在,这会产生编译时警告,但不会在运行时崩溃。这与将数组的泛型类型标记为__kindof
的同一示例之间的主要区别在于,如果您尝试访问其元素,编译器不会抱怨,并将结果存储在类型为@ 的变量中987654343@ 或它的子类之一。
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
【讨论】:
哦该死的,这非常好。这在 WWDC 视频中的某处提到过吗? @ScareCrow 我相信它出现在一些视频中,但在Swift and Objective-C Interoperability 谈话 (link to transcript) 中进行了最详细的讨论。 不正确,可以将UIView
的子类添加到NSArray<UIView *>
。请参阅下面的答案。
@ArtemAbramov 感谢您指出这一点!我已对我的回答进行了必要的更正。
本视频讨论了轻量级泛型:developer.apple.com/videos/play/wwdc2015-401 视频此时讨论了__kindof:developer.apple.com/videos/play/wwdc2015-401/?time=1752【参考方案3】:
iOS9 在 ObjC 上引入了轻量级泛型。 ObjC 是一种真正动态的语言,而 SWIFT 更喜欢静态类型,为了最大限度地提高 ObjC 中的互操作性和类型检查,您可以像这样声明一个数组:
NSArray<UIView *> *views;
这意味着所有的views对象都是UIView对象的实例,想象一下UIView
子视图属性它可以包含不同类型的元素,UIView
s和继承自UIView
的对象,但是编译器会抱怨,因为即使它们继承它们也是不同的类型。那是__kindof
发挥作用。就像说数组包含UIView
类型的对象。
就像仍然使用id
类型的优势,但仅限于一种类。
【讨论】:
+1 我不知道使用__kindof
来允许子类。很遗憾,Apple 推出了一项功能,该功能与当今所有其他语言中的泛型略有不同。【参考方案4】:
@Artem Abramov 是正确的。我想指出一点,正如Objective-C Generics中提到的那样
__kindof
说这个东西必须是 X 或更多派生类。为什么这是必要的?文档没有说出来,但我怀疑是因为 向 Objective-C 添加泛型引入了一个巨大的难题:如果UIView.subviews
现在是NSArray<UIView *>
然后一大堆代码是 现在根据编译器的类型检查器无效:[view.subviews[0] setImage:nil]
是假的,因为 UIView 没有 图像属性。我们已经被 Objective-C 的事实宠坏了 编译器将允许您将任何可见的消息选择器发送到 id... 仅 现在 subviews 数组不返回 id,它返回UIView *
。哎呀。
__kindof
通过说 subviews 数组不是明确的 UIView * 数组而是UIView *
子类数组来解决这个问题。 然后编译器将允许您发送任何可能适用于UIView *
或其所有子类,或从该数组中分配一个元素 到UIImageView *
,但如果您尝试这样做,它仍然会抱怨 这个:NSNumber *n = view.subviews[0]
。
这是 Xcode 7 中引入的 Clang 的一个功能。我认为它与 iOS 版本无关
【讨论】:
【参考方案5】:如果您有一个返回__kindof SomeType *
的函数,那么您可以将该函数的结果分配给SubtypeOfSomeType *
类型的变量,而无需显式强制转换。没有__kindof
,编译器会报错类型不兼容。
其实 UIKit 中至少有一个类似的函数:https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/index.html#//apple_ref/occ/instm/UITableView/dequeueReusableCellWithIdentifier:
现在让我们将知识扩展到NSArray
。在泛型类型参数中指定__kindof
添加__kindof
以返回-objectAtIndex:
和-objectAtIndexedSubscript:
类型(嗯,实际上是返回ObjectType
的泛型NSArray 的任何方法),现在您可以轻松分配调用结果stackView.arrangedSubviews[i]
指向任何类型为 UIView
或 UIView
本身的子类的变量。
重要提示:即使没有__kindof
,您也可以将UIView
子类存储在NSArray<UIView *>
中。 __kindof
只是关于访问数组的元素。
【讨论】:
以上是关于iOS __kindof NSArray?的主要内容,如果未能解决你的问题,请参考以下文章
ios开发ios9新特性关键字学习:泛型,逆变,协变,__kindof
使用“__kindof NSManagedObject * _Nonnull”类型的表达式初始化“NSEntityDescription *”的不兼容指针类型
IOS 中runtime 不可变数组__NSArray0 和__NSArrayI
iOS之NSArray类簇简介-(copymutableCopy导致程序crash)