带有UIApplicationDelegate的假阳性respondsToSelector导致NSInvalidArgumentException

时间:2011-02-14 14:05:44

标签: iphone objective-c uiapplicationdelegate

简而言之,下面的代码调用超类中的现有选择器,然后给出NSInvalidException:

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

这给出了以下日志异常:

  • ***由于未捕获的异常'NSInvalidArgumentException'终止应用程序,原因:' - [aAppDelegate applicationDidEnterBackground:]:无法识别的选择器发送到实例0x5b5d360'

详细说明......我有一个基础应用程序委托(来自我们的新公司库)声明为:

我有一个基本应用程序委托类BaseAppDelegate。它被声明为:

@interface CoAppDelegate : NSObject <UIApplicationDelegate> 

它实现:

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    DebugLog(@"*** ACTIVE ****");
}

它没有实现@selector(applicationWillResignActive :) - 或者至少我的意思是我没有专门为该方法编写代码。它在.h或.m文件中找不到。

我的应用程序有一个应用程序委托,它继承自CoAppDelegate:

@interface aAppDelegate : CoAppDelegate <UIApplicationDelegate>

我将以上两种方法实现为:

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

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

当应用程序启动时,我得到调试输出“*** ACTIVE ****” - 应该这样。

当我将我的应用程序发送到后台时,我得到NSInvalidArgumentException,指出响应者不存在 - 并且它不存在,所以这是正确的抛出异常。

我需要知道的是,为什么当我期待看到NO时,responseToSelector会给出YES?我错过了什么微妙的东西?

3 个答案:

答案 0 :(得分:9)

出于instancesRespondToSelector:

中所述的以下原因,您应该使用documentation
  

通过使用respondsToSelector:关键字将super发送到对象,您无法测试对象是否从其超类继承方法。

     

这个方法仍然会测试整个对象,而不仅仅是超类的实现。因此,将respondsToSelector:发送到super相当于将其发送到self。相反,您必须直接在对象的超类上调用NSObject类方法instancesRespondToSelector:

您的子类'代码应如下所示:

- (void)applicationWillResignActive:(UIApplication *)application {
    if ([[self superclass] instancesRespondToSelector:_cmd])
    {
        [super applicationWillResignActive:application];
    }
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    if ([[self superclass] instancesRespondToSelector:_cmd])
    {   
        [super applicationDidBecomeActive:application];
    }
}

答案 1 :(得分:9)

而不是[super class],您应该使用[self superclass]

[[self superclass] instancesRespondToSelector:@selector(method)]

答案 2 :(得分:0)

[[self superclass] instancesRespondToSelector:<selector>];
在某些特殊情况下,

可能会产生不良结果。最好明确说明类名而不是self:

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

<强>解释

考虑例子:

@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

想象一下接下来的代码:

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

这段代码......崩溃了!为什么?让我们深入研究在C实例上调用bar方法时所发生的事情&#39; c&#39;。 [self superclass]返回... B,因为self是C的实例。当然,B实例响应bar,所以输入if的主体。但是,[超级栏]尝试从B的角度调用超级实现,所以尝试在A上调用bar,这会导致崩溃!

这就是为什么我建议用精确的[B超类]替换[self superclass] - 这解决了问题。