2011-01-01 8 views
18

Según lo que he aprendido hasta ahora: en Objective-C puede enviar cualquier mensaje a cualquier objeto. Si el objeto implementa el método correcto, se ejecutará, de lo contrario no pasará nada. Esto se debe a que antes de enviar el mensaje, Objective-C realizará respondsToSelector.Objective-C respondsToSelector

Espero tener razón hasta ahora.

Hice un pequeño programa para probar donde se invoca una acción cada vez que se mueve un deslizador. También para probar establezco el remitente a NSButton, pero de hecho es un NSSlider. Ahora le pregunté al objeto si responderá a setAlternateTitle. Mientras que un NSButton lo hará y NSSlider no lo hará. Si ejecuto el código y lo hago respondsToSelector mismo me dirá que el objeto no responderá a ese selector. Si pruebo algo más como intValue, responderá. Entonces mi código está bien hasta ahora.

- (IBAction)sliderDidMove:(id)sender 
{ 
    NSButton *slider = sender; 

    BOOL responds = 
    [slider respondsToSelector:@selector(setAlternateTitle)]; 

    if(responds == YES) 
    { 
     NSLog(@"YES");   
    } 
    else 
    { 
     NSLog(@"NO"); 
    } 

    [slider setAlternateTitle:@"Hello World"]; 
} 

Pero cuando realmente enviar el mensaje setAlternateTitle el programa se bloqueará y no estoy exactamente seguro de por qué. ¿No debería hacer un respondsToSelector antes de enviar el mensaje?

Respuesta

146

En primer lugar, el nombre de un método (su selector) incluye todas las subpartes y caracteres de dos puntos, como dijo mvds.

En segundo lugar, el método de ejecución -respondsToSelector: no es llamado por el tiempo de ejecución, generalmente lo llama el usuario (usted o las API que desean saber si un delegado responde, por ejemplo, a un método opcional del protocolo).

Cuando envía un mensaje a un objeto, el tiempo de ejecución buscará la implementación del método en la clase del objeto (a través del puntero isa del objeto). Es equivalente a enviar -respondsToSelector: aunque el mensaje en sí no se envía. Si la implementación del método se encuentra en la clase o en sus superclases, se invoca con todos los argumentos que pasó.

De lo contrario, el tiempo de ejecución le da al mensaje una segunda oportunidad de ejecución. Comenzará enviando el mensaje + (BOOL)resolveInstanceMethod:(SEL)name a la clase del objeto: este método le permite agregar el método en tiempo de ejecución a la clase: si este mensaje devuelve SÍ, significa que puede volver a enviar el mensaje.

Si no le da al mensaje una tercera oportunidad de ejecución, envía - (id)forwardingTargetForSelector:(SEL)aSelector con el selector, este método puede devolver otro objeto que pueda responder al selector en nombre del receptor real, si el objeto devuelto puede responder, el método se ejecuta y el valor se devuelve como si fuera devuelto por el mensaje original. (Nota: Esto está disponible comenzando con OS X 10.6 o iOS 4.)

Si el objeto devuelto es nulo o uno mismo (para evitar bucles infinitos), el tiempo de ejecución le da al mensaje una cuarta oportunidad para ejecutar el método ... Envía el mensaje - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector para obtener una firma de método para construir una invocación. Si se proporciona uno, se envía una invocación a través del mensaje - (void)forwardInvocation:(NSInvocation *)anInvocation. En este método, puede analizar la invocación y crear otros mensajes para enviar a otros destinos de la forma que desee, y luego puede establecer el valor de retorno de la invocación ... Ese valor actuará como el valor de retorno del mensaje original.

Finalmente, si el objeto no devuelve una firma de método, entonces el tiempo de ejecución envía el mensaje - (void)doesNotRecognizeSelector:(SEL)aSelector a su objeto, la implementación de este método en la clase NSObject arroja una excepción.

+0

+1 para esta excelente explicación. ¿Puede proporcionar algún enlace donde todo este proceso esté documentado en detalle? Vale la pena leer – taskinoor

+4

Los documentos están aquí: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html en parte ... Otras cosas están en la referencia de clase NSObject – Psycho

+1

por cierto , 'forwardingTargetForSelector:' está solo en Mac OS X 10.6+ o iOS 4+ – user102008

7

Por un lado, el selector no es solo el "nombre" del mensaje, sino también lo que sigue, es decir, los argumentos y sus nombres.

Así que el selector correcta para algunos -(void)setAlternateTitle:(NSString*)str sería

@selector(setAlternateTitle:) 

con el :

cuanto a su problema: Si una clase respondsToSelector() y de llevar a cabo ese selector, no debe someterse a una bloqueo al enviar un selector desconocido. ¿Qué tipo de registro de bloqueo ves en la ventana de depuración?

(ps. ¿Por qué no incluye el [slider setAlternateTitle:...] en el bloque condicional if (responds) { ... }?)

+0

Más exactamente, un selector no incluye _ "lo que sigue, es decir, los argumentos, y sus nombres" _, sino "el número y la posición de los argumentos dentro del nombre del mensaje". –

2

"Esto se debe a que antes de que el mensaje es enviado Objective-C se realice respondsToSelector."

Supongo que esto no es correcto. Si el objeto no responde al selector, se bloqueará en el tiempo de ejecución. No hay verificación automática por parte del sistema. Si hubo una verificación por parte del sistema de tiempo de ejecución, nunca deberíamos obtener la excepción "selector no reconocido enviado a la instancia".

Por favor, corrígeme si me equivoco.

EDITAR: Esto no es un bloqueo directo, pero el resultado predeterminado es que el proceso finalizará. Toda la secuencia ya está explicada en el comentario y en otra respuesta, así que no voy a escribir eso de nuevo.

+2

No falla, regresa a un proceso llamado * reenvío *. Desafortunadamente, esto no parece estar bien documentado, pero hace varias cosas: primero llama a '+ resolveInstanceMethod:' en la clase del receptor. Si esto falla, llama '-forwardingTargetForSelector:', then '-forwardInvocation:'. La implementación predeterminada de '-forwardInvocation:' arroja una excepción de selector no implementado, que no es lo mismo que un bloqueo. –

+0

@Ahruman, gracias por la explicación. No sabía esta secuencia. En realidad, no es una falla directa como la falla de segmentación, pero el resultado predeterminado parece que el proceso finalizará. – taskinoor

+0

Al menos tenía razón en que el sistema no llama automáticamente al método. – taskinoor

1

Hay un método +instancesRespondToSelector:. Como su nombre lo sugiere, le dice si las instancias de la clase implementan ese método.

Cuestiones relacionadas