False positive responds SoSelector with UIApplicationDelegate, resulting in an NSInvalidArgumentException

In short, the following code raises an existing selector in a superclass, and then throws an NSInvalidException:

- (void)applicationWillResignActive:(UIApplication *)application { if ([super respondsToSelector:@selector(applicationWillResignActive:)]) { [super applicationWillResignActive:application]; } 

This gives the following log exception:

  • *** Application termination due to an unannounced exception "NSInvalidArgumentException", reason: '- [aAppDelegate applicationDidEnterBackground:]: unrecognized selector sent to instance 0x5b5d360'

To develop ... I have a delegate of the base application (from our new corporate library) declared as:

I have a delegation class of the BaseAppDelegate base application. 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 I mean that I did not specifically write code for this method. It cannot be found in the .h or .m file.

In my application, there is an application 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 application starts, I get the debug output "*** ACTIVE ****" - as it should be.

When I send my application to the background, I get this NSInvalidArgumentException, which indicates that the responder does not exist - and it does not exist, so this is the correct exception for throw.

What do I need to know because WHY answer ToSelector gives YES when I expect to see NO? What is the little subtle thing I'm missing?

+7
source share
3 answers

Instead of [super class] you should use [self superclass] :

 [[self superclass] instancesRespondToSelector:@selector(method)] 
+9
source

You should use instancesRespondToSelector: for the following reason specified in the documentation :

You cannot check if an object inherits a method from its superclass by sending respondsToSelector: to the object using the super keyword.

This method will still test the object as a whole, and not just the implementation of the superclass. So sending respondsToSelector: to super equivalent to sending it to self . Instead, you should call the NSObject class instancesRespondToSelector: method directly in the superclass of objects.

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]; } } 
+9
source
 [[self superclass] instancesRespondToSelector:<selector>]; 

may lead to undesirable results in some special cases. It is better to explicitly specify the class name instead of self:

 [[<ClassName> superclass] instancesRespondToSelector:<selector>]; 

Explanation:

Consider an 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 the following code:

 C *c = [C new]; [c bar]; 

This code ... crash! What for? Let me tell you what happens when the bar method is called in an instance of C c. [self superclass] returns ... B, since self is an instance of C. Of course, B instances respond to bar, so an if body is introduced. However, [super bar] tries to call a super implementation from the point of view of B, so it tries to call a bar on A, which leads to a crash!

That's why I suggest replacing [self superclass] with the exact [superclass] [B] that solves the problem.

0
source

All Articles