views:

51

answers:

3

I am working on a project where I have a class which has UIView property. I also define a class which is a subclass of UIView which defines a certain method. If I have the following code, I get a warning when I build:

// In this example, myView is UIView property which *may* contain a UIView or 
// my subclassed-UIView which has the myMethod method
if([myView respondsToSelector:@selector(myMethod)]){
   [myView myMethod]
}

The warning is "UIView may not respond to '-myMethod'". The warning obviously doesn't stop the app from being built, but I am just trying to figure out how to deal with it. Is this the correct way to do this? Is there a way to stop this warning?

+1  A: 

You can use:

[myView performSelector:@selector(myMethod)];
John Franklin
+5  A: 

The warning is only because the compiler doesn't know if that view is your custom subclass. Of course, at runtime it will work fine, since it will be a subclass. You have two options to fix it:

[myView performSelector:@selector(myMethod)];

(So the compiler doesn't check the method call at all)

Or, better:

[(MyViewClass *)myView  myMethod];

That way the compiler acts as if the object really is your view subclass (after you performing the check of course).

For that matter, it might make sense to check for your class rather than the method:

if ([myView isKindOfClass:[MyViewClass class]]) { ...
jtbandes
Checking whether the object responds to the message is generally considered to be better. Asking for the class is a roundabout way to ask if the code will respond to your message.
Chuck
Presumably the idea here is to write code that applies only to the custom subclass. Whether it responds to the selector is relevant, but if you're doing more than one call at once, or just to be sure that your code will do exactly what you expect, checking the class is what I'd do.
jtbandes
Checking for the class is also not enough; I should then check the version of the class (if it is even possible). Comparing it with what done in JavaScript code, it would be like to check the browser used to view a page to verify if a JavaScript function is available.
kiamlaluno
If the purpose is just to be sure the class of object passed to a method is a custom class, then it makes sense to check the class of the object. I would not do that if the class is declared in a framework that is installed separately from the code that checks the object class.
kiamlaluno
Agreed; we're talking about a custom class.
jtbandes
I ended up taking the `performSelector` approach, but appreciated the discussion of other methods. In this case (which wasn't really in my question), I don't need to know what the class is, but just that it has the method I'm after. I agree with Chuck that this is better (in my case).
Rob
+1  A: 

This is a static typing warning, telling you that the type the variable is declared as does not respond to that selector. Since you're actually using a subclass that you've confirmed responds to the selector, you know this isn't a problem, but the compiler isn't smart enough to figure this out. There are a few ways you can fix this. In decreasing order of safety:

  1. Cast the variable to what it actually is that does respond to the selector, either a specific class or a protocol. You'll still need to import the appropriate header or the compiler will suspect you mistyped something. Which option is best depends on your situation (e.g. whether there's one "correct" class to cast to).

    [(id<SomeProtocolWiththatSelector>)myView myMethod];
    [(SomeUIViewSubclass *)myView myMethod];
    
  2. Cast the variable to id to disable static typechecking. You'll still need to import a header with the declaration so the compiler knows some method exists or it will still give the "I'm not sure if this is a real method" warning.

    [(id)myView myMethod];
    
  3. Use performSelector:. This will not do any checks at compile-time, so you don't need to import any headers besides Foundation, but the compiler won't catch any typos either, so any mistakes you make mean the program goes boom at runtime.

    [myView performSelector:@selector(myMethod)];
    
Chuck