通过调用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];
}
}
}
答案 0 :(得分:6)
这取决于具体情况!
我的经验法则是,这只是为了我,还是我将它传递给其他人?
在您的示例中,respondsToSelector:
很好,因为您需要知道的是您是否可以发送该消息的对象,因此您可以对结果执行某些操作。这堂课并不那么重要。
另一方面,如果您要将该对象传递给其他一些代码,您不一定知道它打算发送哪些消息。在这些情况下,您可能会转换对象以便传递它,这可能是一个线索,您应该在投射之前检查它是否真的isKindOfClass:
。
要考虑的另一件事是模棱两可; respondsToSelector:
告诉您对象将响应消息,但如果对象返回的类型与您预期的不同,则可能会生成误报。例如,声明方法的对象:
- (int)sources;
将通过respondsToSelector:
测试,但在尝试在for-in循环中使用其返回值时会生成异常。
这种可能性有多大?这取决于您的代码,项目的大小,针对您的API编写代码的人数等等。
答案 1 :(得分:5)
使用respondsToSelector:
略微更具惯用性。 Objective C是高度动态的,因此您对类结构的设计时间假设可能不一定在运行时保持水。 respondsToSelector:
通过为您提供查询类类型的最常见原因的快捷方式 - 它是否执行某些操作来实现。{/ p>
一般来说,如果围绕一些同样吸引人的选择存在歧义,请考虑可读性。在这种情况下,这意味着考虑意图。你是否关心它是WidgetWithSources
,还是真的只关心它有sources
选择器?如果是后者,则使用respondsToSelector:
。如果是前者,在某些情况下很可能,那么使用isKindOfClass.
可读性,在这种情况下,意味着您不要求读者在WidgetWithSources
的类型等价与需要致电sources
。 respondsToSelector:
为读者提供了这种联系,让他们知道你的意图。这对你的同事程序员来说是一种善意的小动作。
编辑:@ benzado的答案非常一致。
答案 2 :(得分:3)
来自@Tim&的完美答案@benzado,这里是主题的变体,先前涵盖了两个案例:
isKindOfClass:
的情况。例如,颜色可能会作为NSData
存储在首选项中序列化NSColor
,或NSString
值,其中一个标准名称;在这种情况下获取NSColor
值isKindOfClass:
对象返回可能是合适的。respondsToSelector:
例如,许多框架类在以后的版本中添加新方法操作系统和Apple的标准建议是使用respondsToSelector:
检查这些方法(而不是操作系统版本检查)。如果你有不同类的引用,并且你正在测试它们是否符合某些非正式协议那么:
conformsToProtocol:
作为测试。这样做的好处是可以测试类型,而不仅仅是 name ;否则respondsToSelector:
,但我们知道仅测试是否存在具有相同名称的方法,并不是说它需要相同的类型参数。答案 3 :(得分:2)
检查可能是一个警告,您将要制定一个hackish解决方案。小部件已经知道他的班级和他的选择者。
所以第三种选择可能是考虑重构。将此逻辑移动到[widget tryToRefresh]
可能更清晰,并允许将来的小部件实现其他幕后逻辑。