【问题标题】:False positive respondsToSelector with UIApplicationDelegate leading to NSInvalidArgumentException误报 respondsToSelector 与 UIApplicationDelegate 导致 NSInvalidArgumentException
【发布时间】:2011-06-26 22:57:40
【问题描述】:

简而言之,下面的代码调用超类中已有的选择器,然后给出一个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 时,responsToSelector 会给出 YES?我错过了什么微妙的小东西?

【问题讨论】:

  • 我要问你为什么要这样做?你知道你的类的基类的 API,所以你知道它实现和不实现哪些方法。没有必要进行测试。事实上,您不需要实现这些方法。
  • 原因有点复杂,归根结底是因为太新(对于 iPhone,来自 java...),以至于当我无法以一种方式工作时,我会尝试其他方式...在那个周期中重复了几次,我得到了上面的(不必要的)代码。尽管不需要,但它仍然提出了一个我真正想了解的问题(甚至认为“真实”代码是在不使用这种方法的情况下修复的)。在 Java 中,我可以使用反射来做类似的事情,但不同之处在于超类上的反射不会包含子类上的方法。所以我真的只是想了解。

标签: iphone objective-c uiapplicationdelegate


【解决方案1】:

您应该使用instancesRespondToSelector:,原因如下documentation

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

此方法仍将测试整个对象,而不仅仅是超类的实现。因此,将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];
    }
}

【讨论】:

  • 谢谢,雅各布。并投票赞成将我介绍给 _cmd。 (由于这个问题是一个新人类型,这里有一个指向 _cmd 上的一些信息的链接,以便从谷歌搜索中保存下一个人:Secret Life of _cmd
  • @Richard 不客气!我认为它可以很好地用于 DRY 目的,无需将自己包装在自己的选择器中,只需使用 _cmd! :)
  • 所写的代码实际上是行不通的。不幸的是,调用 [super class] 仍然会给出 self 对象的类名。这意味着该代码仍然基本上检查“self”本身是否响应它已经响应的选择器。正如相同的文档所暗示的那样,我将代码更改为直接引用 CoAppDelegate (超类的名称),最后它可以工作了。
【解决方案2】:

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

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

【讨论】:

  • 这可能并不总是正确的 - 请参阅下面的详细答案。
【解决方案3】:
[[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 实例“c”上调用 bar 方法时发生了什么。 [self 超类] 返回... B,因为 self 是 C 的实例。当然,B 实例响应 bar,所以进入 if 的主体。但是,[super bar] 尝试从 B 的角度调用 super 实现,因此尝试在 A 上调用 bar,导致崩溃!

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-20
    • 1970-01-01
    • 2017-05-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多