super respondsToSelector
-
Anyone want to take a stab at explaining to me as thoroughly as
possible why
[super respondsToSelector: aSelector]
doesn't actually do what it looks like it should do?
Bonus points if you can explain why I can override the
respondsToSelector:(SEL)aSelector method in my object and as long as I
include the above line at the end of my overridden version my object
reports back correctly that the object serves as a delegate and the
delegate methods get called appropriately.
It seems like either an infinite loop should occur if
respondsToSelector is really being called on self, or it should return
NO if it's called on super, in which case my delegate method in self
should never get called.
Puzzled. -
On Sat, May 31, 2008 at 1:22 AM, Russ McBride <russ...> wrote:
> It seems like either an infinite loop should occur if respondsToSelector is
> really being called on self, or it should return NO if it's called on super,
> in which case my delegate method in self should never get called.
If you called respondsToSelector: on self inside of your
respondsToSelector:, then yeah, that would be an infinite loop
condition. Otherwise, calling respondsToSelector: on super should work
like any other overridden method.
The respondsToSelector: implementation in NSObject looks at the method
table for the current class, not just the methods that are defined in
NSObject. Perhaps that's where your confusion comes from. From the
documentation:
<file://localhost/Developer/Documentation/DocSets/com.apple.ADC_Reference_Library.CoreReference.docset/Contents/Resources/Documents/documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html#//apple_ref/doc/uid/20000052-BBCEHCCE>
"You cannot test whether an object inherits a method from its
superclass by sending respondsToSelector: to the object using the
super keyword. This method will still be testing the object as a
whole, not just the superclass's implementation. Therefore, sending
respondsToSelector: to super is equivalent to sending it to self.
Instead, you must invoke the NSObject class method
instancesRespondToSelector: directly on the object's superclass, as
illustrated in the following code fragment." -
On 30 May '08, at 11:22 PM, Russ McBride wrote:
> Anyone want to take a stab at explaining to me as thoroughly as
> possible why
> [super respondsToSelector: aSelector]
> doesn't actually do what it looks like it should do?
Sure. "super" does not mean "my superclass". It means "self, but start
looking for method implementations in my superclass".
The purpose of "super" is to give you a way to call the inherited
version of a method you've overridden, since there would otherwise be
no way of doing so. The actual receiver of the method that's called
via "super" is self, since "super" only changes the way in which
methods are looked up.
Since your class didn't override -respondsToSelector:, the
implementation you get when you user "super" is the same as the normal
one you'd get if you called it on self, i.e. the implementation in
NSObject. And since the receiver is self, not some hypothetical
instance of your superclass, you get the same result as if you'd
called [self respondsToSelector:].
If you actually want to ask a question of your class's superclass, you
have to do so directly:
[[[self class] superclass] instancesRespondToSelector: aSelector]
—Jens -
On 30 May 08, at 23:22, Russ McBride wrote:
> Anyone want to take a stab at explaining to me as thoroughly as
> possible why
> [super respondsToSelector: aSelector]
>
> doesn't actually do what it looks like it should do?
Keep in mind that, ordinarily, respondsToSelector: is implemented on
NSObject. It does introspection of some sort on self - so [super
respondsToSelector:...] is identical to [self respondsToSelector:...]
unless you've implemented the method yourself. -
On May 31, 2008, at 1:22 AM, Russ McBride wrote:
>
> Anyone want to take a stab at explaining to me as thoroughly as
> possible why
> [super respondsToSelector: aSelector]
>
> doesn't actually do what it looks like it should do?
>
> Bonus points if you can explain why I can override the
> respondsToSelector:(SEL)aSelector method in my object and as long
> as I include the above line at the end of my overridden version my
> object reports back correctly that the object serves as a delegate
> and the delegate methods get called appropriately.
>
> It seems like either an infinite loop should occur if
> respondsToSelector is really being called on self, or it should
> return NO if it's called on super, in which case my delegate method
> in self should never get called.
In general, sending a message to an object tells the Objective-C
runtime to search for the proper implementation of that method. The
search starts at the real class of the object and proceeds up the
inheritance chain through the superclasses. It invokes the first one
it finds.
Sending a message to "super" tells the Objective-C runtime to do the
same search, but start at the superclass of the class which defined
the method which is currently executing.
Now, if none of the classes in the inheritance chain have overridden -
respondsToSelector:, then the only implementation to find (in either
case) is the one provided by NSObject. That implementation always
behaves the same way -- it doesn't know and can't tell that it's been
invoked on super (nor from what class's code that might have happened
in). It just does what it's designed to do: it determines if "self"
responds to the given selector.
So, a message to super _doesn't_ do any of the following:
1) message a different object than self
2) cause self to masquerade as a more limited or restricted version
of itself (i.e. it doesn't actually change whether self responds to a
given selector, even temporarily)
3) invoke the implementation that's eventually found any differently
than normal (i.e. there's no hidden parameter which passes along
which class is "targeted")
It just starts the search for the implementation in a different
spot. If both searches (starting from self's real class or starting
from the superclass of the currently executing code) find the same
implementation, then the result is identical.
To do what you want, you can use [MySuperClass
instancesRespondToSelector:aSelector]. Note, you have to name the
specific class you want to check. You can't use [self superclass]
because that's dynamic -- the result from that may actually be deeper
in the class hierarchy than the code you're writing.
I hope that helps.
Cheers,
Ken -
On May 30, 2008, at 11:43 PM, Jens Alfke wrote:
> If you actually want to ask a question of your class's superclass,
> you have to do so directly:
> [[[self class] superclass] instancesRespondToSelector: aSelector]
Well... yeah... you *can* do that.
But *don't do that*. (And I know Jens knows better -- I'm just
pointing something out).
Any code that relies upon *skipping* super's implementation of
something to get something done is just asking for trouble.
b.bum -
Responding to myself:
On May 31, 2008, at 1:52 AM, Ken Thomases wrote:
> To do what you want, you can use [MySuperClass
> instancesRespondToSelector:aSelector]. Note, you have to name the
> specific class you want to check. You can't use [self superclass]
> because that's dynamic -- the result from that may actually be
> deeper in the class hierarchy than the code you're writing.
I was reminded by some googling that a better thing to do, rather
than naming your superclass explicitly ("MySuperClass" in my example)
is to name your own class explicitly and use the +superclass method
to find the superclass:
[[MyClass superclass] instancesRespondToSelector:aSelector]
Cheers,
Ken -
On 30 May '08, at 11:55 PM, Bill Bumgarner wrote:
> On May 30, 2008, at 11:43 PM, Jens Alfke wrote:
>> If you actually want to ask a question of your class's superclass,
>> you have to do so directly:
>> [[[self class] superclass] instancesRespondToSelector: aSelector]
>
> Well... yeah... you *can* do that.
>
> But *don't do that*. (And I know Jens knows better -- I'm just
> pointing something out).
>
> Any code that relies upon *skipping* super's implementation of
> something to get something done is just asking for trouble.
No, that's not what it's for. There are situations where you implement
a method, and you want to call the superclass implementation ... but
only if there _is_ one, since otherwise that would raise an exception.
This comes about with those @$&% "informal protocols" that pollute the
namespace of NSObject. So for example, let's say I subclass
NSFoobarControl and implement -awakeFromNib. Now, should I call [super
awakeFromNib] first? If NSFoobarControl implements that method, then
yes I'd damn better do so, otherwise I screw up the superclass's
state. But if it doesn't, that super call will raise an exception.
One solution is to put in the 'super' call, and then try running your
code. If that call blows up, take it out. That's really brittle,
though, since who knows if NSFoobarControl will change in 10.6 to have
an -awakeFromNib method? If it does, suddenly my app might crash on
10.6, and that might be really hard to track down.
The only reasonable solution seems to be to do exactly what I said —
check whether the superclass implements the method before calling it.
—Jens -
On Sat, May 31, 2008 at 2:52 PM, Ken Thomases <ken...> wrote:
> To do what you want, you can use [MySuperClass
> instancesRespondToSelector:aSelector]. Note, you have to name the specific
> class you want to check. You can't use [self superclass] because that's
> dynamic -- the result from that may actually be deeper in the class
> hierarchy than the code you're writing.
Just as a minor nit, it's probably better to write [[MyClass
superclass] instancesRespondToSelector:...]. This variant will survive
if you re-target your superclass and forget to change what it depends
on. Both variants will still fail if you subject your code to
copypasta without sufficient vetting.
If you're adventurous and foolish you can extract your superclass
without needing to hard-code any class names:
#import <objc/objc-runtime.h>
...
Class superclass = ((struct objc_super *)super)->class;
But this is probably a poor idea to use in practice....
Mike -
On May 31, 2008, at 12:10 AM, Michael Ash wrote:
> If you're adventurous and foolish you can extract your superclass
> without needing to hard-code any class names:
>
> #import <objc/objc-runtime.h>
> ...
> Class superclass = ((struct objc_super *)super)->class;
>
> But this is probably a poor idea to use in practice....
Why not simply:
[[self superclass] instancesRespondToSelector:...]
?
j o a r -
On Sat, May 31, 2008 at 3:16 PM, j o a r <joar...> wrote:
>
> On May 31, 2008, at 12:10 AM, Michael Ash wrote:
>
>> If you're adventurous and foolish you can extract your superclass
>> without needing to hard-code any class names:
>>
>> #import <objc/objc-runtime.h>
>> ...
>> Class superclass = ((struct objc_super *)super)->class;
>>
>> But this is probably a poor idea to use in practice....
>
>
> Why not simply:
>
> [[self superclass] instancesRespondToSelector:...]
>
> ?
Assume the following class hierarchy:
A <- B <- C <- D <- E
In the implementation of B, you write [self superclass]. Now your
method executes with self set to an instance of E. What do you get?
Mike -
On May 31, 2008, at 12:23 AM, Michael Ash wrote:
> Assume the following class hierarchy:
>
> A <- B <- C <- D <- E
>
> In the implementation of B, you write [self superclass]. Now your
> method executes with self set to an instance of E. What do you get?
I would propose that you should re-examine your design if you find
that to be a problem in practice:
<http://en.wikipedia.org/wiki/Code_smell>
j o a r -
On May 31, 2008, at 2:43 AM, j o a r wrote:
>
> On May 31, 2008, at 12:23 AM, Michael Ash wrote:
>
>> Assume the following class hierarchy:
>>
>> A <- B <- C <- D <- E
>>
>> In the implementation of B, you write [self superclass]. Now your
>> method executes with self set to an instance of E. What do you get?
>
>
> I would propose that you should re-examine your design if you find
> that to be a problem in practice:
>
> <http://en.wikipedia.org/wiki/Code_smell>
Using [self superclass] is wrong. The hierarchy with five levels may
or may not be a problem, but [self superclass] is still wrong even in
a hierarchy with 3 levels (even if "A" is NSObject, as is typical).
If your class may be subclassed, then [[self superclass]
instancesRespondToSelector:aSelector] may blow up in your face.
-Ken -
On Sat, May 31, 2008 at 3:43 PM, j o a r <joar...> wrote:
>
> On May 31, 2008, at 12:23 AM, Michael Ash wrote:
>
>> Assume the following class hierarchy:
>>
>> A <- B <- C <- D <- E
>>
>> In the implementation of B, you write [self superclass]. Now your
>> method executes with self set to an instance of E. What do you get?
>
>
> I would propose that you should re-examine your design if you find that to
> be a problem in practice:
>
> <http://en.wikipedia.org/wiki/Code_smell>
As has been noted, this is a common problem in AppKit code. It happens
any time you subclass a subclass of NSView and implement
-awakeFromNib. If you then subclass that subclass (something which
doesn't smell to me even a little bit) then the naive solution breaks
badly.
Mike


