In short, the following code calls an existing selector in the super class, and then gives an NSInvalidException:
- (void)applicationWillResignActive:(UIApplication *)application {
if ([super respondsToSelector:@selector(applicationWillResignActive:)])
{
[super applicationWillResignActive:application];
}
This gives the following log exception:
- *** Terminating app开发者_JS百科 due to uncaught exception 'NSInvalidArgumentException', reason: '-[aAppDelegate applicationDidEnterBackground:]: unrecognized selector sent to instance 0x5b5d360'
To elaborate... I have a base application delegate (from our new company library) declared as:
I have a base application delegate class, BaseAppDelegate. It is declared as:
@interface CoAppDelegate : NSObject <UIApplicationDelegate>
It implements:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
DebugLog(@"*** ACTIVE ****");
}
It does not implement @selector(applicationWillResignActive:) - or at least what I mean is that I have not specifically written out code for that method. It can't be found in the .h or .m file.
My app has an app delegate that inherits from CoAppDelegate as:
@interface aAppDelegate : CoAppDelegate <UIApplicationDelegate>
I implement both of the above methods as:
- (void)applicationWillResignActive:(UIApplication *)application {
if ([super respondsToSelector:@selector(applicationWillResignActive:)])
{
[super applicationWillResignActive:application];
}
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
if ([super respondsToSelector:@selector(applicationDidBecomeActive:)])
{
[super applicationDidBecomeActive:application];
}
}
When the app launches, I get the debug output "*** ACTIVE ****" - as it should.
When I send my app to the background I get that NSInvalidArgumentException stating that the responder does not exist - and it does not exist, so this is the correct exception to throw.
What I need to know is WHY does respondsToSelector give a YES when I am expecting to see a NO? What is the little subtle thing that I am missing?
Instead of [super class]
you should use [self superclass]
:
[[self superclass] instancesRespondToSelector:@selector(method)]
You should use instancesRespondToSelector:
for the following reason stated in the documentation:
You cannot test whether an object inherits a method from its superclass by sending
respondsToSelector:
to the object using thesuper
keyword.This method will still be testing the object as a whole, not just the superclass’s implementation. Therefore, sending
respondsToSelector:
tosuper
is equivalent to sending it toself
. Instead, you must invoke theNSObject
class methodinstancesRespondToSelector:
directly on the object’s superclass.
Your subclass' code should look like this:
- (void)applicationWillResignActive:(UIApplication *)application {
if ([[self superclass] instancesRespondToSelector:_cmd])
{
[super applicationWillResignActive:application];
}
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
if ([[self superclass] instancesRespondToSelector:_cmd])
{
[super applicationDidBecomeActive:application];
}
}
[[self superclass] instancesRespondToSelector:<selector>];
may produces undesired results in some special cases. It is better to explicitly state class name instead of self:
[[<ClassName> superclass] instancesRespondToSelector:<selector>];
Explanation:
Consider example:
@protocol MyProtocol <NSObject>
@optional
- (void)foo;
- (void)bar;
@end
@interface A : NSObject <MyProtocol>
@end
@implementation A
- (void)foo {
//Do sth
}
@end
@interface B : A
@end
@implementation B
- (void)bar {
//B may not know which methods of MyProtocol A implements, so it checks
if ([[self superclass] instancesRespondToSelector:@selector(bar)]) {
[super bar];
}
//Do sth
}
@end
@interface C : B
@end
@implementation C
@end
Imagine then following code:
C *c = [C new];
[c bar];
This code ... crashes! Why? Let's dig through what's going on when calling bar method on C instance 'c'. [self superclass] returns... B, since self is instance of C. Of course, B instances reponds to bar, so the body of if is entered. However, [super bar] tries to call super implementation from B's perspective, so tries to call bar on A, which results in crash!
That's why I suggest to replace [self superclass] with precise [B superclass] - which solves the problem.
精彩评论