Is it more appropriate to check a class's type by calling isKindOfClass:, or take the "duck typing" approach by just checking whether it supports the method you're looking for via respondsToSelector: ?
Here's the code I'm thinking of, written both ways:
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];
}
}
}
Alternatively:
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];
}
}
}
It's slightly more idiomatic Objective C to use respondsToSelector:
. Objective C is highly dynamic, so your design time assumptions about class structure may not necessarily hold water at run time. respondsToSelector:
gets round that by giving you a shortcut to the most common reason for querying the type of a class - whether it performs some operation.
In general where there's ambiguity around a couple of equally appealing choices, go for readability. In this case that means thinking about intent. Do you care if it's specifically a WidgetWithSources
, or do you really just care that it has a sources
selector? If it's the latter, then use respondsToSelector:
. If the former, and it may well be in some cases, then use isKindOfClass.
Readability, in this case, means that you're not asking the reader to make the connection between type equivalence of WidgetWithSources
and the need to call sources
. respondsToSelector:
makes that connection for the reader, letting them know what you actually intended. It's a small act of kindness towards your fellow programmer.
Edit: @benzado's answer is nicely congruent.
It depends on the situation!
My rule of thumb would be, is this just for me, or am I passing it along to someone else?
In your example, respondsToSelector:
is fine, since all you need to know is whether you can send the object that message, so you can do something with the result. The class isn't really that important.
On the other hand, if you were going to pass that object to some other piece of code, you don't necessarily know what messages it will be intending to send. In those cases, you would probably be casting the object in order to pass it along, which is probably a clue that you should check to see if it really isKindOfClass:
before you cast it.
Another thing to consider is ambiguity; respondsToSelector:
tells you an object will respond to a message, but it could generate a false positive if the object returns a different type than you expect. For example, an object that declares a method:
- (int)sources;
Would pass the respondsToSelector:
test but then generate an exception when you try to use its return value in a for-in loop.
How likely is that to happen? It depends on your code, how large your project is, how many people are writing code against your API, etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With