2009-06-22 9 views
10

Tengo dos clases que pueden actuar como delegadas de una tercera clase, y ambas implementan un protocolo formal hecho completamente de métodos opcionales. Una de las clases implementa todo mientras que otra solo implementa un par de métodos que me importan. Sin embargo, en el tiempo de ejecución cuando tengo la segunda clase actúo como delegado a la tercera clase, y la tercera clase llama a uno de los métodos opcionales no implementados en ese delegado, obtengo un error de tiempo de ejecución que básicamente dice "El objetivo no responde a esto selector de mensajes ". Pensé que Object-c manejó este caso correctamente, y que simplemente no haría nada si ese método no estuviera realmente definido en la clase. ¿Podría haber algo que me falta?¿Por qué los métodos de protocolo opcional no implementados causan errores de tiempo de ejecución cuando se llama a ese método en obj-c?

Respuesta

33

Cuando se llama a un método opcional de su delegado, es necesario asegurarse de que responde al selector antes de llamar a:

if ([delegate respondsToSelector:@selector(optionalMethod)]) 
    [delegate optionalMethod]; 
+2

me lo imaginaba, pero esperaba que no necesitaría añadir esos controles if en todo el código. Gracias por el puntero. – Kevlar

10

conocer los protocolos opcionales significar simplemente el objeto que implementa el protocolo no tiene que poner en práctica el método en cuestión: el destinatario debe verificar si el objeto implementa el método antes de llamar (de lo contrario, se bloqueará, como habrá notado). Estas categorías NSObject HOM pueden ser útiles:

@implementation NSObject (Extensions) 

- (id)performSelectorIfResponds:(SEL)aSelector 
{ 
    if ([self respondsToSelector:aSelector]) { 
     return [self performSelector:aSelector]; 
    } 
    return NULL; 
} 

- (id)performSelectorIfResponds:(SEL)aSelector withObject:(id)anObject 
{ 
    if ([self respondsToSelector:aSelector]) { 
     return [self performSelector:aSelector withObject:anObject]; 
    } 
    return NULL; 
} 

@end 

, entonces puede simplemente hacer:

[delegate performSelectorIfResponds:@selector(optionalMethod)]; 
+0

nice suggestions :) – Kevlar

+0

........ ¿Cuál es HOM? – bandejapaisa

+0

HOM = Mensajes de orden superior. –

1

bloques podría proporcionar una mejor solución. Permiten realizar condicionalmente cualquier código basado en la existencia de una implementación de un método dado:

-(void) performBlock:(void (^)(void))block ifRespondsTo:(SEL) aSelector{ 
    if ([self respondsToSelector:aSelector]) { 
     block(); 
    } 
} 

Mediante el uso de esta adición a NSObject, puede ejecutar condicionalmente cualquier método @optional, no importa cuántos parámetros que podría tener .

Ver How to safely send @optional protocol messages that might not be implemented

4

Esta solución Bloques funciona bien, una vez que obtenga su cabeza envuelta en torno a lo que está pasando. Agregué un resultado BOOL porque quería poder ejecutar condicionalmente uno de varios métodos opcionales. Algunos consejos si está tratando de implementar esta solución:

Primero, si aún no ha encontrado extensiones/categorías, simplemente agregue esto al principio de su clase, FUERA de la definición de clase existente. Será una extensión pública o privada según el lugar donde la coloque.

@implementation NSObject (Extensions) 
// add block-based execution of optional protocol messages 
-(BOOL) performBlock:(void (^)(void))block ifRespondsTo:(SEL) aSelector 
{ 
    if ([self respondsToSelector:aSelector]) { 
     block(); 
     return YES; 
    } 
    return NO; 
} 
@end 

En segundo lugar, así es como usted lo llama desde el código:

BOOL b = [(NSObject*)self.delegate performBlock:^{ 
    // code to run if the protocol method is implemented 
} 
ifRespondsTo:@selector(Param1:Param2:ParamN:)]; 

Reemplazar Param1: Param2: paramN: con los nombres de cada parámetro para el método de protocolo. Cada uno debe terminar con dos puntos. Así que si su método de protocolo se parece a:

-(void)dosomething:(id)blah withObj:(id)blah2 andWithObj(id)blah3;

la última línea se vería así:

ifRespondsTo:@selector(dosomething:withObj:andWithObj:)];

Cuestiones relacionadas