UIApplicationDelegate 误报 respondsToSelector 导致 NSInvalidArgumentException

发布于 2024-10-17 11:15:58 字数 1695 浏览 2 评论 0原文

简而言之,以下代码调用超类中的现有选择器,然后给出 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 ,指出响应者不存在 - 并且它不存在,因此这是抛出的正确异常。

我需要知道的是,为什么当我期望看到“否”时,respondsToSelector 却给出“是”?我缺少的微妙的小东西是什么?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

云雾 2024-10-24 11:15:58

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

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

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

[[self superclass] instancesRespondToSelector:@selector(method)]
注定孤独终老 2024-10-24 11:15:58

您应该使用 instancesRespondToSelector: 原因如下 文档

您无法通过使用 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];
    }
}

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];
    }
}
与往事干杯 2024-10-24 11:15:58
[[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 superclass] 返回... B,因为self是C的实例。当然,B实例响应bar,所以进入if的主体。然而,[super bar] 尝试从 B 的角度调用 super 实现,因此尝试在 A 上调用 bar,这会导致崩溃!

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文