Super отвечаетToSelector: возвращает true, но на самом деле вызов super (selector) дает "непризнанный селектор, отправленный в экземпляр",
Хорошо, я немного смущен.
У меня есть подкласс UIScrollView, который является моей попыткой горизонтального прокручивания "табличного представления", такого как элемент пользовательского интерфейса. Сам UIScrollView настраивает UIGestureRecognizers, который он использует внутри себя, и, похоже, он выступает в роли делегата для этих UIGestureRecognizers. У меня также есть моя собственная настройка UIGestureRecognizer для моих горизонтальных элементов/ячеек таблицы и мой собственный набор классов в качестве делегата для моего собственного UIGestureRecognizer. Поскольку мой класс является подклассом UIScrollView, во время выполнения вызовы делегатов UIGestureRecognizer поступают в мой класс для встроенных UIGestureRecognizers UIScrollView и моих собственных UIGestureRecognizers. Немного о PITA, но мы можем обойти это, передавая те, которые нам не нужны.
-(BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]])
return NO;
else
{
if ([super respondsToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)])
return [super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
else
return NO;
}
}
Проблема в том, что проверка [super respondsToSelector:@selector()]
возвращает YES, но когда я на самом деле называю ее return [super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
, я получаю следующее исключение
2012-08-31 12: 02: 06.156 MyApp [35875: 707] - [MyAppHorizontalImageScroller gestureRecognizer: shouldRecognizeSimultaneousWithGestureRecognizer:]: непризнанный селектор, отправленный в экземпляр 0x21dd50
Я бы подумал, что он должен показать
- [UIScrollView gestureRecognizer: shouldRecognizeSimultaneousWithGestureRecognizer:]
Но это может быть ОК. Но проблема в том, что он говорит, что он отвечает, а затем нет.
Другие два подпрограммы делегатов UIGestureRecognizer работают с этим кодом (очевидно, разные селекторы).
Спасибо за понимание.
Ответы
Ответ 1
Если вы не отменяете ответ на селектор в своем классе, вы будете использовать реализацию по умолчанию, когда вы вызываете супер, который будет проверять текущий экземпляр. Если вы хотите увидеть, отвечает ли экземпляр типа объекта на селектор +(BOOL)instancesRespondToSelector:(SEL)aSelector;
Это проверит объект и его родительские объекты. Поэтому в вашем случае вы хотите следующее:
[UIScrollView instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]
Ответ 2
[super respondsToSelector:@selector(frobnosticate:)]
не делает того, что вы думаете.
Он переходит в суперкласс, получает там реализацию respondsToSelector:
, а затем запускает его на текущем объекте. Другими словами, super
представляет тот же объект, что и self
, он просто запускает поиск метода на один шаг выше в дереве наследования.
Итак, вы используете respondsToSelector:
в этом подклассе, который отвечает "YES", но затем пытается получить gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
из суперкласса, который его не имеет.
Чтобы проверить экземпляры непосредственного суперкласса, вы должны использовать instancesRespondToSelector:
, как рекомендует jjburka, но я бы предложил [self superclass]
как предмет этого, например:
[[self superclass] instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)];
который позволяет избежать имен классов жесткого кодирования.
Ответ 3
Посмотрев на другие ответы, лучшим решением будет использование [[MapControllerSublcass1 superclass] instancesRespondToSelector:_cmd]
. Если вы используете то, что рекомендуется выше, что-то вроде [BaseClass instancesRespondToSelector:_cmd]
, вы сталкиваетесь с проблемой изменения иерархии классов и случайно забываете изменить BaseClass
на новый суперкласс или ваш подкласс.
[[self superclass] instancesRespondToSelector:...]
неверно, как объяснялось выше в комментариях, и на самом деле так говорится Документация Apple (см. responsesToSelector: в NSObjct). Он работает только тогда, когда у вас есть 1 уровень подкласса, поэтому он дает вам иллюзию, что это фактическое решение. Я влюбился в это.
И [[super class] instancesRespondToSelector:...]
не работает и является целым пунктом этого вопроса SO.
Например, у меня есть BaseMapController
, который реализует некоторые из методов в MKMapViewDelegate
, но не реализует mapView:regionWillChangeAnimated:
. MapControllerSublcass1
наследуется от BaseMapController
. И MapControllerSubclass2
наследует от MapControllerSublcass1
.
В моем коде у меня есть что-то вроде этого, и он отлично работает.
MapControllerSublcass1.m
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
if ([[MapControllerSublcass1 superclass] instancesRespondToSelector:_cmd]) {
[super mapView:mapView regionWillChangeAnimated:animated];
}
}
MapControllerSubclass2.m
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
if ([[MapControllerSubclass2 superclass] instancesRespondToSelector:_cmd]) {
[super mapView:mapView regionWillChangeAnimated:animated];
}
}
Ответ 4
Когда вы вызываете
[super respondsToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]
Это переход к суперклассу и выполнение его реализации respondsToSelector
. Это рассмотрит экземпляр (в данном случае ваш пользовательский просмотр прокрутки) и определяет, отвечает ли он этому селектору или нет.
Когда вы вызываете
[super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
Вы пытаетесь отправить сообщение, используя реализацию суперкласса этого метода, которая в этом случае не существует, что приводит к сбою.
Похоже, что jjburka добрался до него первым - вам нужно позвонить
[UIScrollView instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]
Кроме того, он не будет сбой, если вы используете -[super performSelector:]
из нераспознанного селектора - выполнить селектор получает экземпляры реализации селектора. Он будет терпеть крах из бесконечной рекурсии.
Ответ 5
Как резюме для кого-то с одним и тем же случаем, в исходном вопросе есть две проблемы:
- Проверка соответствия суперкласса этому селектору, который, как предлагает @jjburka, лучше всего использовать с помощью
instancesRespondToSelector:
.
- Фактически вызывать метод суперкласса без компилятора, даже если он объявлен в закрытом заголовке. Это можно аккуратно реализовать, обновив его в категории (см. этот вопрос).
Объединяя это, получаем:
// … In subclass implementation file
@interface UIScrollView () <UIGestureRecognizerDelegate> @end
// … In gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
if ([UIScrollView instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]) {
return [super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
}