Objective-C:检查类类型,最好使用isKindOfClass或respondsToSelector?

时间:2012-07-16 18:30:12

标签: objective-c

通过调用isKindOfClass:检查类的类型是否更合适,或者通过仅通过respondsToSelector检查它是否支持您正在寻找的方法来采用“duck typing”方法:?

以下是我正在考虑的代码,两种方式:

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];
        }
    }
}

4 个答案:

答案 0 :(得分:6)

这取决于具体情况!

我的经验法则是,这只是为了我,还是我将它传递给其他人?

在您的示例中,respondsToSelector:很好,因为您需要知道的是您是否可以发送该消息的对象,因此您可以对结果执行某些操作。这堂课并不那么重要。

另一方面,如果您要将该对象传递给其他一些代码,您不一定知道它打算发送哪些消息。在这些情况下,您可能会转换对象以便传递它,这可能是一个线索,您应该在投射之前检查它是否真的isKindOfClass:

要考虑的另一件事是模棱两可; respondsToSelector:告诉您对象将响应消息,但如果对象返回的类型与您预期的不同,则可能会生成误报。例如,声明方法的对象:

- (int)sources;

将通过respondsToSelector:测试,但在尝试在for-in循环中使用其返回值时会生成异常。

这种可能性有多大?这取决于您的代码,项目的大小,针对您的API编写代码的人数等等。

答案 1 :(得分:5)

使用respondsToSelector:略微更具惯用性。 Objective C是高度动态的,因此您对类结构的设计时间假设可能不一定在运行时保持水。 respondsToSelector:通过为您提供查询类类型的最常见原因的快捷方式 - 它是否执行某些操作来实现。{/ p>

一般来说,如果围绕一些同样吸引人的选择存在歧义,请考虑可读性。在这种情况下,这意味着考虑意图。你是否关心它是WidgetWithSources,还是真的只关心它有sources选择器?如果是后者,则使用respondsToSelector:。如果是前者,在某些情况下很可能,那么使用isKindOfClass.可读性,在这种情况下,意味着您不要求读者在WidgetWithSources的类型等价与需要致电sourcesrespondsToSelector:为读者提供了这种联系,让他们知道你的意图。这对你的同事程序员来说是一种善意的小动作。

编辑:@ benzado的答案非常一致。

答案 2 :(得分:3)

来自@Tim&的完美答案@benzado,这里是主题的变体,先前涵盖了两个案例:

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

    1. 如果这是您控制的代码,则可以切换到正式协议,然后使用conformsToProtocol:作为测试。这样做的好处是可以测试类型,而不仅仅是 name ;否则
    2. 如果这是您无法控制的代码,请使用respondsToSelector:,但我们知道测试是否存在具有相同名称的方法,并不是说它需要相同的类型参数。

答案 3 :(得分:2)

检查可能是一个警告,您将要制定一个hackish解决方案。小部件已经知道他的班级和他的选择者。

所以第三种选择可能是考虑重构。将此逻辑移动到[widget tryToRefresh]可能更清晰,并允许将来的小部件实现其他幕后逻辑。