Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

False positive respondsToSelector with UIApplicationDelegate leading to NSInvalidArgumentException

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 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?

like image 630
Richard Le Mesurier Avatar asked Feb 14 '11 14:02

Richard Le Mesurier


2 Answers

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

[[self superclass] instancesRespondToSelector:@selector(method)]
like image 147
Robert Wijas Avatar answered Nov 16 '22 02:11

Robert Wijas


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 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.

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];
    }
}
like image 9
Jacob Relkin Avatar answered Nov 16 '22 03:11

Jacob Relkin