Ложный положительный отклик ToSelector с UIApplicationDelegate, приводящий к NSInvalidArgumentException - PullRequest
7 голосов
/ 14 февраля 2011

Короче говоря, следующий код вызывает существующий селектор в суперклассе, а затем выдает 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, утверждающее, что ответчик не существует - и он не существует, поэтому это правильное исключение, которое нужно выкинуть.

Что мне нужно знать, так это то, почему responsedsToSelector выдает YES, когда я ожидаю увидетьНЕТ?Что это за тонкая вещь, которую мне не хватает?

Ответы [ 3 ]

9 голосов
/ 03 октября 2011

Вместо [super class] вы должны использовать [self superclass]:

[[self superclass] instancesRespondToSelector:@selector(method)]
9 голосов
/ 14 февраля 2011

Вы должны использовать instancesRespondToSelector: по следующей причине, указанной в документации :

Вы не можете проверить, наследует ли объект метод от своего суперкласса, отправив 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];
    }
}
0 голосов
/ 04 июля 2014
[[self superclass] instancesRespondToSelector:<selector>];

может привести к нежелательным результатам в некоторых особых случаях. Лучше явно указывать имя класса, а не себя:

[[<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];

Этот код ... вылетает! Зачем? Давайте разберемся, что происходит при вызове метода bar в экземпляре C 'c'. [self superclass] возвращает ... B, поскольку self является экземпляром C. Конечно, экземпляры B отвечают на bar, поэтому вводится тело if. Однако [super bar] пытается вызвать супер реализацию с точки зрения B, поэтому пытается вызвать bar на A, что приводит к сбою!

Именно поэтому я предлагаю заменить [self суперкласс] на точный [B суперкласс] - что решает проблему.

...