Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-C: Checking class type, better to use isKindOfClass, or respondsToSelector?

Tags:

objective-c

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];
        }
    }
}
like image 772
Greg Maletic Avatar asked Jul 16 '12 18:07

Greg Maletic


2 Answers

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.

like image 25
Tim Avatar answered Oct 04 '22 02:10

Tim


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.

like image 169
benzado Avatar answered Oct 04 '22 04:10

benzado