Greg Maletic
Greg Maletic

Reputation: 6337

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

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

Upvotes: 5

Views: 1995

Answers (4)

CRD
CRD

Reputation: 53000

Good answers from @Tim & @benzado, here is a variation on the theme, the previously covered two cases first:

  • If at some point you have may have a reference to distinct classes and need them differently then this is probably a case for isKindOfClass: For example, an color might be stored in preferences as either an NSData serialization on an NSColor, or as an NSString value with one of the standard names; to obtain the NSColor value in this case isKindOfClass: on the object return is probably appropriate.
  • If you have a reference to a single class but different versions of it over time have supported different methods then consider respondsToSelector: For example, many framework classes add new methods in later versions of the OS and Apple's standard recommendation is to check for these methods using respondsToSelector: (and not an OS version check).
  • If you have a reference to distinct classes and you are testing if they adhere to some informal protocol then:

    1. If this is code you control you can switch to a formal protocol and then use conformsToProtocol: as your test. This has the advantage of testing for type and not just name; otherwise
    2. If this is code you do not control then use respondsToSelector:, but we aware that this is only testing that a method with the same name exists, not that it takes the same types of arguments.

Upvotes: 3

waggles
waggles

Reputation: 2854

Checking either might be a warning that you are about to make a hackish solution. The widget already knows his class and his selectors.

So a third option might be to consider refactoring. Moving this logic to a [widget tryToRefresh] may be cleaner and allow future widgets to implement additional behind the scenes logic.

Upvotes: 2

Tim
Tim

Reputation: 5054

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.

Upvotes: 5

benzado
benzado

Reputation: 84328

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.

Upvotes: 6

Related Questions