Objective-C:检查类类型,更好的方法是使用isKindOfClass还是respondsToSelector?

5

检查一个类的类型,使用isKindOfClass:方法更合适呢?还是采用“鸭子类型”方法,只需要通过respondsToSelector:方法检查它是否支持你要查找的方法?

以下是我考虑的代码,两种方式都有:

for (id widget in self.widgets)
{
    [self tryToRefresh:widget];

    // Does this widget have sources? Refresh them, too.
    if ([widget isKindOfClass:[WidgetWithSources class]])
    {
        for (Source* source in [widget sources])
        {
            [self tryToRefresh:source];
        }
    }
}

或者:

for (id widget in self.widgets)
{
    [self tryToRefresh:widget];

    // Does this widget have sources? Refresh them, too.
    if ([widget respondsToSelector:(@selector(sources))])
    {
        for (Source* source in [widget sources])
        {
            [self tryToRefresh:source];
        }
    }
}

请查看此主题:https://dev59.com/ulTTa4cB1Zd3GeqPoxGl - Ivor Prebeg
请注意,通常不建议动态检查类的类型。在委托模式之外,响应选择器也是如此。 - bbum
4个回答

6
这取决于情况!
我的经验法则是,这只是为了我自己,还是要传递给别人?
在你的例子中,respondsToSelector: 是可以的,因为你只需要知道是否可以向对象发送该消息,以便对结果进行操作。类并不是非常重要。
另一方面,如果你要将该对象传递给其他代码,你不一定知道它将要发送什么消息。在这种情况下,你可能会强制转换对象以便传递它,这可能是一个提示,你应该在强制转换之前检查它是否真的是KindOfClass:。
还有一件事需要考虑的是歧义; respondsToSelector: 告诉你一个对象将响应一个消息,但如果对象返回与你期望的不同类型,则可能会生成错误的结果。例如,声明了一个方法的对象:
- (int)sources;

该对象可能通过respondsToSelector:测试,但是当您尝试在for-in循环中使用其返回值时会生成异常。

这种情况发生的可能性有多大?它取决于您的代码,项目的规模,有多少人编写针对您的API的代码等因素。


5
使用respondsToSelector:稍微更符合Objective C的习惯。Objective C是高度动态的,所以你在设计时对类结构的假设在运行时可能并不成立。respondsToSelector:通过为您提供一种查询类是否执行某些操作的最常见原因的快捷方式来解决这个问题。
通常,在两个同样具有吸引力的选择之间存在歧义时,应选择易读性更好的选项。在这种情况下,这意味着考虑意图。你关心它是否特别是一个WidgetWithSources,还是你真正关心它是否具有sources选择器?如果是后者,则使用respondsToSelector:。如果前者,这在某些情况下可能是正确的,则使用isKindOfClass。 在这种情况下,易读性意味着您不需要要求读者将WidgetWithSources的类型等价与调用sources的需求联系起来。respondsToSelector:为读者建立了这种联系,让他们知道你实际上的意图。这是为您的同行程序员提供的小小的善举。
编辑:@benzado的答案非常一致。

3

@Tim和@benzado提供了好的答案,这是主题的变体,首先涵盖了两种情况:

  • 如果您在某个时刻可能拥有对不同类的引用,并且需要以不同的方式使用它们,则可能需要使用isKindOfClass:。例如,颜色可以作为NSData序列化或NSColor的一个NSString值存储在首选项中;在此情况下,可能适合在对象返回的isKindOfClass:上进行NSColor值获取。
  • 如果您只有对单个类的引用,但随着时间的推移,其不同版本支持了不同的方法,则考虑使用respondsToSelector:。例如,许多框架类在操作系统的后续版本中添加新方法,Apple的标准建议是使用respondsToSelector:(而不是OS版本检查)检查这些方法。
  • 如果您具有对不同类的引用,并且正在测试它们是否遵守某些非正式协议,请执行以下操作:

    1. 如果这是您控制的代码,则可以切换到正式协议,然后使用conformsToProtocol:作为测试。这具有测试类型而不仅是名称的优点;否则
    2. 如果这是您不能控制的代码,则使用respondsToSelector:,但要注意,这仅测试具有相同名称的方法是否存在,并不意味着它们需要采取相同的参数类型

2

检查任何一个可能会警示你即将使用hackish解决方案。小部件已经知道他的类和选择器。

因此,第三个选项可能是考虑重构。将这个逻辑移动到[widget tryToRefresh]可能更清晰,并允许未来的小部件实现额外的后台逻辑。


所有的答案都很好,但是对于我来说,这个才是正确的。我能够重新实现并且完全摆脱这段代码! - Greg Maletic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接