__kindof 和不在 Objective-C 中使用的区别
Posted
技术标签:
【中文标题】__kindof 和不在 Objective-C 中使用的区别【英文标题】:Difference Between __kindof and not using it in Objective-C 【发布时间】:2015-11-19 08:03:28 【问题描述】:我已阅读article 关于 ios 中 Objective-C 的一些新功能。但是,我不知道这两种方式的主要区别是什么:
@property (strong, nonatomic, nonnull) NSArray<UIView *> *someViews;
和
@property (strong, nonatomic, nonnull) NSArray<__kindof UIView *> *someViews;
对我来说,它们看起来非常相似。有什么区别,什么时候应该使用一个而不是另一个?
【问题讨论】:
【参考方案1】:要查看__kindof
的全部效果,我建议您直接使用它并查看不同的结果:
NSMutableArray<UIView *> *views;
NSMutableArray<__kindof UIView *> *subviews;
views = [NSMutableArray new];
subviews = [NSMutableArray new];
UIView *someView = [UIView new];
[views addObject:someView];
[subviews addObject:someView];
UIButton *someSubview = [UIButton new];
[views addObject:someSubview];
[subviews addObject:someSubview];
到目前为止,对于插入不同的泛型数组。编译和运行都很好。没有警告,没有崩溃。
然而,有趣的部分是从数组中读取数据 - 请记住,两个数组的第一个插槽是实际的 UIView *
,第二个插槽是 UIButton *
UIView *extView00 = views[0];
UIView *extView01 = subviews[0];
UIView *extView10 = views[1];
UIView *extView11 = subviews[1];
UIButton *extButton00 = views[0]; <-- warning
UIButton *extButton01 = subviews[0];
UIButton *extButton10 = views[1]; <-- warning
UIButton *extButton11 = subviews[1];
这会正常运行,但会针对标记的行给出两个编译器警告:
使用“UIView *”类型的表达式初始化“UIButton *”的不兼容指针类型
其他两行按预期工作。当然仍然没有崩溃。但是我们遇到了一些问题:extButton01
包含一个UIView *
,但看起来像一个UIButton *
。
因此添加以下内容
NSLog(@"%@", extButton00.titleLabel);
NSLog(@"%@", extButton01.titleLabel);
NSLog(@"%@", extButton10.titleLabel);
NSLog(@"%@", extButton11.titleLabel);
在第一行和第二行按预期崩溃。如果我们删除整个 views
数组,我们将得到无警告但崩溃的代码。而且我不喜欢崩溃但无警告的代码。当然,无警告并不能保证没有崩溃,但为了方便而删除警告并不是一个好主意,恕我直言。
结论
是的,该功能非常适合移除演员表。 但是它还删除了可能对不匹配类型有用的警告。如果您 100% 确定索引 X 处的对象属于 T 类型,那么您可以使用 subviews
方法,即使用 __kindof
。
就我个人而言,我不会/不会使用它 - 直到我遇到一个我还没有看到的非常非常好的用例。
【讨论】:
【参考方案2】:一切都很简单。 在第一种情况下,您需要编写:
UILabel* titleLabel = (typeof(titleLabel))someViews[0];
在第二个你可以写:
UILabel* titleLabel = someViews[0];
您应该选择您喜欢的方式:隐式或显式类型转换。
【讨论】:
我认为这实际上是正确的答案,但我仍然觉得这是一个令人作呕的功能。你为什么要那个?你不能确定位置 0 有 UILabel,对吗?如何?因为数组中只有标签?那为什么一开始就声明它包含 UIViews 呢? “隐式向下转换”似乎是这里的流行语。编译器会知道可能有一些后代并进行适当的转换,如果它为调用的方法找到合适的接口会很高兴。 @Eiko 但是如果第一个插槽中实际上没有 UILabel 会发生什么? @luk2302 我完全同意你的看法。自动下垂的东西是危险的方便。另一方面,它没有比手动向下转换更多的问题,只是没有样板。 @luk2302 在 Objective-C 中,我们通常不指定数组包含的对象的类型根本。数组可以同时包含字符串和数字以及视图和视图控制器。将字符串和视图放在同一个数组中通常不是很有用,因此大多数时候您可以获得有关对象类型的更多信息。但是在同一个集合中拥有几种不同类型的视图通常很有用。告诉编译器它可以期望组中的每个对象都是某种视图,这样可以提高安全性,而不会使代码变得笨拙。【参考方案3】:您知道如何声明像id foo
这样的变量来指示 foo 指向任何类型的对象的指针,然后您可以向foo
发送任何消息而不会引起编译器的抱怨吗? kindof__ UIView* foo
就是这样——它允许您指定 foo
是某种“类型”视图,但您可以向 foo
发送任何消息,该消息对任何类型的视图都有效,而无需强制转换。例如:
UIView *bar = [[UIButton alloc] initWithFrame:CGRectZero];
[bar setTarget:nil];
在这种情况下,编译器会抱怨第二行,因为-setTarget:
不是UIView
中的方法。如果你想编译它,你必须像这样:
[(UIButton*)bar setTarget:nil];
__kindof
让您避免这种情况:
__kindof UIView *foo = [[UIButton alloc] initWithFrame:CGRectZero];
[foo setTarget:nil];
在这里你告诉编译器foo
可以是任何类型的视图,这让它稍微放松一点,让你调用-setTarget:
而不必将其转换为UIButton*
。您可以将foo
声明为id foo
并获得类似的效果,但指定foo
是某种视图可为编译器提供更多可使用的信息。具体来说,它可以编写与 Swift 兼容的 Objective-C 代码,Swift 的类型比 Objective-C 更强。
【讨论】:
使用 id foo 您可以调用 [foo length] 而编译器不会抱怨,然后编译器会在运行时崩溃。使用 __kindof UIView* foo 你不能。 __kindof 只是 id 的一个更好的版本。 @gasher729 如果这是真的,那就太好了,但是如果你写[foo length]
,编译器实际上不会抱怨——它的行为就像 id
,真的,而且你仍然只是在运行时获得无法识别的选择器异常。以上是关于__kindof 和不在 Objective-C 中使用的区别的主要内容,如果未能解决你的问题,请参考以下文章
使用“__kindof NSManagedObject * _Nonnull”类型的表达式初始化“NSEntityDescription *”的不兼容指针类型
ios开发ios9新特性关键字学习:泛型,逆变,协变,__kindof
The monkey is ______ smart. 为啥写kind of 快点吧!亲们!