objective c - super respondsToSelector: devuelve true pero, en realidad, llamar a super(selector) da "un selector no reconocido enviado a la instancia"
objective-c delegates (5)
Está bien, estoy un poco confundido.
Tengo una subclase de UIScrollView, que es mi intento de una "vista de tabla" de desplazamiento horizontal como el elemento UI. UIScrollView configura UIGestureRecognizers que usa internamente, y parece configurarse como el delegado de esos UIGestureRecognizers. También tengo mi propia configuración de UIGestureRecognizer en los elementos / celdas de mi tabla horizontal y mi propia clase configurada como delegado para mi propio UIGestureRecognizer. Como mi clase es una subclase de UIScrollView, en tiempo de ejecución, las llamadas de delegado UIGestureRecognizer vienen a mi clase para los UIScrollView incorporados UIGestureRecognizers y mis propios UIGestureRecognizers Un poco de PITA, pero podemos solucionar esto pasando los que no nos importan.
-(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;
}
}
El problema es que el cheque [super respondsToSelector:@selector()]
devuelve SÍ, pero cuando en realidad lo llamo return [super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
Me sale la siguiente excepción
2012-08-31 12: 02: 06.156 MyApp [35875: 707] - [MyAppHorizontalImageScroller gestoRecognizer: shouldRecognizeSimultaneouslyWithGestureRecognizer:]: selector no reconocido enviado a la instancia 0x21dd50
Habría pensado que debería mostrar
- [UIScrollView gestoRecognizer: shouldRecognizeSimultaneouslyWithGestureRecognizer:]
Pero eso puede estar bien. Pero el problema es que dice que responde y luego no.
Las otras dos rutinas de delegado UIGestureRecognizer funcionan con este código (obviamente, diferentes selectores).
Gracias por cualquier idea
A menos que la anulación responda al selector en su clase, utilizará la implementación predeterminada cuando llame a super, que verificará la instancia actual. Si desea ver si una instancia de un tipo de objeto responde a un selector use +(BOOL)instancesRespondToSelector:(SEL)aSelector;
Esto comprobará el objeto y sus objetos principales. Así que en tu caso quieres lo siguiente:
[UIScrollView instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]
Como resumen para alguien con el mismo caso, hay dos problemas en la pregunta original:
- Verificando si la superclase responde a ese selector, que como se sugiere por @jjburka se realiza mejor usando
instancesRespondToSelector:
- En realidad, se invoca el método en la superclase sin que el compilador se queje a pesar de que se declara en un encabezado privado. Esto se puede lograr de manera prolija redeclarándolo en una categoría (vea esta pregunta ).
Juntándolo todo esto da:
// … In subclass implementation file
@interface UIScrollView () <UIGestureRecognizerDelegate> @end
// … In gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
if ([UIScrollView instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]) {
return [super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
}
Cuando usted llama
[super respondsToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]
Esto va a la superclase y ejecuta su implementación de respondsToSelector
. Esto examina la instancia (en este caso, su vista de desplazamiento personalizado) y determina si responde a ese selector o no.
Cuando usted llama
[super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
Intenta enviar un mensaje utilizando la implementación de superclase de ese método, que en este caso no existe, lo que provoca el bloqueo.
Parece que jjburka lo consiguió primero, debes llamar
[UIScrollView instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]
Además, no se bloqueará si utiliza -[super performSelector:]
de un selector no reconocido - el selector de ejecución obtiene la implementación de las instancias del selector. Se estrellará de la recursión infinita.
Después de ver las otras respuestas, la mejor solución es usar [[MapControllerSublcass1 superclass] instancesRespondToSelector:_cmd]
. Si usa lo que se recomienda anteriormente, algo como [BaseClass instancesRespondToSelector:_cmd]
, se encuentra con el problema de cambiar su jerarquía de clases y olvidarse accidentalmente de cambiar BaseClass
a la nueva superclase o su subclase.
[[self superclass] instancesRespondToSelector:...]
es incorrecto como se explicó anteriormente en los comentarios y en realidad lo dice en developer.apple.com/library/ios/#documentation/Cocoa/Reference/… . Solo funciona cuando tienes 1 nivel de subclases, por lo que te da la ilusión de que es una solución real. Me enamoré de eso.
Y [[super class] instancesRespondToSelector:...]
no funciona y es el punto central de esta pregunta SO.
Por ejemplo, tengo un BaseMapController
que implementa algunos de los métodos en MKMapViewDelegate
, pero no implementa mapView:regionWillChangeAnimated:
MapControllerSublcass1
hereda de BaseMapController
. Y MapControllerSubclass2
hereda de MapControllerSublcass1
.
En mi código tengo algo como esto, y funciona bien.
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];
}
}
[super respondsToSelector:@selector(frobnosticate:)]
no hace lo que piensas.
Va a la superclase, obtiene la implementación de respondsToSelector:
allí, y luego la ejecuta en el objeto actual . En otras palabras, super
representa el mismo objeto que self
, simplemente inicia la búsqueda del método un paso más arriba en el árbol de herencia.
Así que está ejecutando respondsToSelector:
en esta subclase, que responde "SÍ", pero luego intenta obtener gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
con el gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
de la superclase, que no lo tiene.
Para verificar las instancias de la superclase inmediata, debería usar instancesRespondToSelector:
como recomienda jjburka, pero sugeriría [self superclass]
como el tema de eso, así:
[[self superclass] instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)];
lo que evita los nombres de clase de codificación.