Multiple methods found

2008-03-14 04:56:18 UTC

I wrote some code like this:

[[QTMovie movieNamed:sound error:NULL] play];

and Xcode gave me three warnings:

  • GrowlApplicationController.m:537: warning: multiple methods named ‘-play’ found

    • /Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/AppKit.framework/Headers/NSSound.h:47: warning: using ‘-(BOOL)play
    • /Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/QTKit.framework/Headers/QTMovie.h:340: warning: also found ‘-(void)play

All warnings are bad, but these are even worse: The compiler is telling me that it’s picked the wrong method. I’m creating a QTMovie, so I want it to use -[QTMovie play] (the one that returns void), but it picked -[NSSound play] (the one that returns BOOL).

Now, in this case, it won’t matter, thanks to Obj-C’s dynamism—all it will do is send a play message to the object, and the object will use whatever play method it knows about. In other words, despite what the compiler is telling me, the program will actually call the correct method (the one in QTMovie).

But this could be a lot worse. If I’m expecting, say, a structure type, and the compiler picks a method that returns a scalar type such as int or float (or vice versa), the compiler will write the wrong assembly code for the message. My method will get gibberish back from the method it called, and Bad Things will follow.

The reason for this  problem is that +movieNamed:error: returns an id—not a QTMovie * specifically. For all the compiler knows, that method returns an NSSound *, or even an NSUserDefaultsController *.

Since the compiler doesn’t know what type the method returns, it must pick one from all the methods by that name that it knows about. You can’t rely on it picking any particular one, and I could only guess as to how it makes its choice. For all I know, it’s blind chance.

The ugly and naïve solution would be to tell the compiler what type we’re expecting by casting it:

[(QTMovie *)[QTMovie movieNamed:sound error:NULL] play];

Of course, besides being ugly, this specific code still has the problem of not accepting an error object. What I should do, and will do, is accept the error object and stash the returned movie in a variable, so that I can test it and then either report the error or play the movie.

NSError *error = nil;
QTMovie *movie = [QTMovie movieNamed:soundName error:&error];
if (movie)
    [movie play];
else
    [NSApp presentError:error];

This code is longer, but it will always use the correct method and it doesn’t ignore the error. (You may also have noticed that I change the previously-misleading name of the name variable from sound to soundName.)

Leave a Reply

Do not delete the second sentence.