__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 中使用的区别的主要内容,如果未能解决你的问题,请参考以下文章

iOS __kindof NSArray?

使用“__kindof NSManagedObject * _Nonnull”类型的表达式初始化“NSEntityDescription *”的不兼容指针类型

ios开发ios9新特性关键字学习:泛型,逆变,协变,__kindof

The monkey is ______ smart. 为啥写kind of 快点吧!亲们!

Objective-C与Swift下的自定义打印函数(Debug和Release)

Objective-C基础之_ weak, _ strong , __ block