Cómo deshacerse de la advertencia de 'selector no declarado'

162

Quiero usar un selector en una instancia de NSObject sin la necesidad de un protocolo implementado. Por ejemplo, hay un método de categoría que debería establecer una propiedad de error si la instancia de NSObject en la que se invoca lo admite. Este es el código, y el código funciona según lo previsto:

if ([self respondsToSelector:@selector(setError:)])
{
    [self performSelector:@selector(setError:) withObject:[NSError errorWithDomain:@"SomeDomain" code:1 userInfo:nil]];
}

Sin embargo, el compilador no ve ningún método con setError: signature, por lo que me da una advertencia, para cada línea que contiene el @selector(setError:)fragmento:

Undeclared selector 'setError:'

No quiero tener que declarar un protocolo para deshacerme de esta advertencia, porque no quiero que todas las clases que pueden usar esto implementen algo especial. Solo por convención quiero que tengan un setError:método o propiedad.

¿Es esto factible? ¿Cómo?

Saludos,
EP

epologee
fuente
2
La solución es bien explicado en performSelector puede causar una fuga debido a su selector se desconoce
loretoparisi
Un selector en desuso causará la advertencia. Ya no es seguro acceder al selector porque el selector podría eliminarse en algún momento.
DawnSong

Respuestas:

254

Otra opción sería deshabilitar la advertencia con:

#pragma GCC diagnostic ignored "-Wundeclared-selector"

Puede colocar esta línea en el archivo .m donde se produce la advertencia.

Actualizar:

Funciona también con LLVM así:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"

... your code here ...

#pragma clang diagnostic pop
Klaas
fuente
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" // Do your thing #pragma clang diagnostic pop
mareado
Sí, lo hace como dice @dizy. (Perdón por la respuesta tardía, pero me perdí la notificación).
Klaas
Alson también necesitaba#pragma clang diagnostic ignored "-Wselector"
máximo el
1
@mdorseif La mayoría de las veces, la advertencia de que 'tiene que excluir' aparece en el registro de compilación. Puede silenciar cualquier advertencia con este concepto. Me alegro de que hayas añadido el tuyo con respecto a los selectores.
Klaas
@epologee puede hacer lo mismo a través de la configuración de compilación "Selector no declarado"
194

Echa un vistazo a NSSelectorFromString .

 SEL selector = NSSelectorFromString(@"setError:");
 if ([self respondsToSelector:selector])

Le permitirá crear un selector en tiempo de ejecución, en lugar de en tiempo de compilación a través de la @selectorpalabra clave, y el compilador no tendrá ninguna posibilidad de quejarse.

sergio
fuente
Hola @sergio, tanto tus respuestas como las de @jacobrelkin funcionan. Prácticamente presentado simultáneamente. ¿Me ayudarán a elegir la "mejor" respuesta, si hay alguna?
epologee
2
Me gusta más esta respuesta porque parece más "Cacao" -y (?). La sel_registerName()cosa parece oscura y del tipo que no deberías llamar directamente a menos que sepas lo que estás haciendo, como obj_msg_send ();)
Nicolas Miari
15
No estoy seguro si es Xcode 5, pero recibo una advertencia diferente con esta implementación: "PerformSelector puede causar una fuga porque su selector es desconocido" .
Hampden123
1
@ Hampden123: ese es un tema diferente. eche un vistazo aquí: stackoverflow.com/questions/7017281/…
sergio el
52

Creo que esto se debe a que por alguna extraña razón, el selector no está registrado en el tiempo de ejecución.

Intente registrar el selector a través de sel_registerName():

SEL setErrorSelector = sel_registerName("setError:");

if([self respondsToSelector:setErrorSelector]) {
   [self performSelector:setErrorSelector withObject:[NSError errorWithDomain:@"SomeDomain" code:1 userInfo:nil]];
}
Jacob Relkin
fuente
Hola @jacobrelkin, tanto tus respuestas como las de @ sergio funcionan. Prácticamente presentado simultáneamente. ¿Me ayudarán a elegir la "mejor" respuesta, si hay alguna?
epologee
2
@epologee NSSelectorFromStringllama sel_registerName()bajo el capó de todos modos. Elija el que más le convenga.
Jacob Relkin
1
@epologee Creo que llamar sel_registerName()directamente es más explícito sobre por qué lo estás haciendo. NSSelectorFromStringno te dice que va a intentar registrar el selector.
Jacob Relkin
8
No estoy seguro si es Xcode 5, pero recibo una advertencia diferente con esta implementación: "PerformSelector puede causar una fuga porque su selector es desconocido" .
Hampden123
@ Max_Power89 No. Vea mis otros comentarios a continuación. No quería pasar demasiado tiempo en esto, así que simplemente incluí los archivos de encabezado.
Hampden123
7

Recibí ese mensaje para que desapareciera # incluyendo el archivo con el método. No se utilizó nada más de ese archivo.

Mark Patterson
fuente
Si bien esta es una solución menos elegante, funciona para mí, ya que tengo los "sospechosos conocidos" que podrían estar recibiendo el selector. Además, si implemento el enfoque del selector de tiempo de ejecución, todavía recibiría una advertencia diferente en la instrucción performSelector; a saber, "PerformSelector puede causar una fuga porque su selector es desconocido" . ¡Así que gracias!
Hampden123
2
Ninguna de las respuestas más votadas es correcta. La intención de la advertencia de "selector no declarado" es detectar errores en tiempo de compilación si cambia el nombre del selector en el que confiaba. Por lo tanto, es más correcto # importar el archivo que declara el método en el que confiaba.
Brane
7

Me doy cuenta de que llego un poco tarde a este hilo, pero para completar, puede desactivar esta advertencia globalmente utilizando la configuración de compilación de destino.

En la sección, 'Advertencias de Apple LLVM - Objetivo-C', cambie:

Undeclared Selector - NO
Quijote
fuente
6

Si su clase implementa el método setError: (incluso declarando dinámicamente el establecedor de la propiedad de error eventual) es posible que desee declararlo en su archivo de interfaz (.h), o si no desea mostrarlo de esa manera, podría prueba con el truco complicado PrivateMethods:

@interface Yourclass (PrivateMethods)

- (void) yourMethod1;
- (void) yourMethod2;

@end

justo antes de su @implementation, esto debería ocultar las advertencias;).

i_mush
fuente
Gracias, pero estoy llamando al método desde una categoría, por lo que esto no se aplica. Saludos, EP.
epologee
Y algunos de nosotros estamos haciendo cosas que son más exóticas: el selector se implementa en un objeto F #, en mi caso.
James Moore
1
Esto no elimina la advertencia en XCode 7.1.1 / iOS 9.1, puedo verPerformSelector may cause a leak because its selector is unknown
loretoparisi
3

Una macro muy cómodo para poner en su .pcho Common.hcualquier lugar al que desea:

#define SUPPRESS_UNDECLARED_SELECTOR_LEAK_WARNING(code)                        \
_Pragma("clang diagnostic push")                                        \
_Pragma("clang diagnostic ignored \"-Wundeclared-selector"\"")     \
code;                                                                   \
_Pragma("clang diagnostic pop")                                         \

Es una edición de esta pregunta para un problema similar ...

Aviel Gross
fuente
3

Puede desactivarlo en Xcode como en la captura de pantalla:

ingrese la descripción de la imagen aquí

Hola Mundo
fuente
Buena esa. Aún así, prefiero deshabilitar la advertencia solo para casos explícitos, al decir "el sonido de claxon está mal en esta ocasión, sé lo que estoy haciendo". ¡Gracias por tu contribución!
epologee
2

También puede lanzar el objeto en cuestión a una identificación primero para evitar la advertencia:

if ([object respondsToSelector:@selector(myMethod)]) {
    [(id)object myMethod];
}
Estafador
fuente
1
Esto no elimina la misma advertencia en el contenido de la expresión if, hasta XC7.1 hasta el día de hoy.
Martin-Gilles Lavoie
2

Otra forma de evitar esta advertencia es asegurarse de que su método de selección tenga este aspecto:

-(void) myMethod :(id) sender{
}

No olvide "remitente (id)" si desea aceptar cualquier remitente o especificar un tipo de objeto de remitente si lo prefiere.

Reposo
fuente
0

Si bien la respuesta correcta probablemente radica en informar a Xcode a través de las importaciones o registrar el selector que dicho selector existe, en mi caso me faltaba un punto y coma. Antes de "corregir" el error, asegúrese de que tal vez el error sea correcto y su código no lo sea. Encontré el error en la muestra MVCNetworking de Apple, por ejemplo.

Louis St-Amour
fuente
No, la respuesta correcta no fue informar a Xcode a través de las importaciones, porque esas importaciones estaban en su lugar. La respuesta correcta fue la respuesta anterior marcada como ... la respuesta correcta, aunque la respuesta de @ sergio también resolvería el problema. El uso del selector incorrecto no es el tema de esta pregunta, por lo tanto, cambiar el selector no es una respuesta. Sin embargo, te ahorraré el voto negativo.
epologee
1
Gracias por recordarme que probablemente debería haber usado un comentario. Todo lo que puedo decir es que las importaciones faltantes también causan esta advertencia de Xcode, si no esta instancia específica. Solo recomendaría NSSelectorFromString u otras opciones de "registro" similares al crear un selector en tiempo de ejecución o responder a las llamadas a métodos de forma dinámica (por ejemplo, methodSignatureForSelector). Registrarlo significa que está "solucionando el error" y, por lo tanto, no es correcto en algunas circunstancias, ya que un enfoque más correcto sería corregir la advertencia (si el análisis de ruido es correcto, claro)
Louis St-Amour
De hecho, ahora veo que la pregunta original dice claramente "sin la necesidad de un protocolo implementado", y no menciona las importaciones en absoluto. Por lo tanto, afirmaría que importar la categoría en sí podría ser la mejor opción para este usuario. Cualquier otra cosa aquí podría definir el selector dos veces, técnicamente hablando. ¿Si? - Editar: Ah, he llevado esto demasiado lejos. Gracias por su respuesta, me detendré ahora. :)
Louis St-Amour
-1

Pude obtener la advertencia para que desapareciera agregando otro método (divulgación: no pensé en esto, pero lo encontré buscando en googlettimetimerwithtimeinterval)

    [NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow]
                                     target:self
                                   selector:@selector(donothingatall:)
                                   userInfo:nil
                                    repeats:YES];


    [[NSRunLoop currentRunLoop] run];

    HTTPLogVerbose(@"%@: BonjourThread: Aborted", THIS_FILE);

    }
}

+ (void) donothingatall:(NSTimer *)timer
{

}

Si bien aprecio saber cómo ocultar la advertencia, corregirla es mejor y ni las técnicas de Sergio ni de Relkin funcionaron para mí, por razones desconocidas.

usuario938797
fuente
1
Si alguien más lee esta solución, que funcionará , él / ella estará bastante confundido, incluido usted mismo en el futuro. Si está seguro de saber lo que está haciendo llamando a un selector no existente, lo que provoca una advertencia, omita el código auxiliar del método engañoso y asegúrese de que su código exprese su intención.
epologee
1
Buen punto. Estaba trabajando con código heredado y solo trataba de descubrir cómo hacer que la advertencia desapareciera, sin tratar de resolver la pregunta básica de por qué tener un selector inexistente. Un paso a la vez, siempre digo.
user938797