Como se livrar do aviso 'seletor não declarado'

162

Eu quero usar um seletor em uma instância NSObject sem a necessidade de um protocolo implementado. Por exemplo, há um método de categoria que deve definir uma propriedade de erro se a instância NSObject em que é chamada oferecer suporte. Este é o código, e o código funciona como pretendido:

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

No entanto, o compilador não vê nenhum método com a assinatura setError:, portanto, é exibido um aviso para cada linha que contém o @selector(setError:)trecho:

Undeclared selector 'setError:'

Não quero declarar um protocolo para se livrar desse aviso, porque não quero que todas as classes que possam usar isso implementem algo especial. Apenas por convenção, quero que eles tenham um setError:método ou propriedade.

Isso é factível? Quão?

Saúde,
EP

epologado
fonte
Um seletor obsoleto causará o aviso. Não é mais seguro acessar o seletor porque o seletor pode ser removido em algum momento.
DawnSong

Respostas:

254

Outra opção seria desativar o aviso com:

#pragma GCC diagnostic ignored "-Wundeclared-selector"

Você pode colocar essa linha no arquivo .m onde ocorre o aviso.

Atualizar:

Também funciona com o LLVM assim:

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

... your code here ...

#pragma clang diagnostic pop
Klaas
fonte
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" // Do your thing #pragma clang diagnostic pop
dizy
sim, ele faz como afirma @dizy. (Desculpe pela resposta tardia, mas perdi a notificação).
Klaas
Eu alson necessário#pragma clang diagnostic ignored "-Wselector"
max
1
@mdorseif Na maioria das vezes, o aviso que você precisa excluir é listado no log de compilação. Você pode silenciar qualquer aviso com esse conceito. Que bom que você adicionou o seu em relação aos seletores.
Klaas #
@epologee você pode fazer o mesmo através da configuração de compilação "Undeclared Selector"
194

Dê uma olhada no NSSelectorFromString .

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

Isso permitirá que você crie um seletor em tempo de execução, em vez de em tempo de compilação, através da @selectorpalavra - chave, e o compilador não terá chance de reclamar.

sergio
fonte
Olá @sergio, as respostas da sua e da jacobrelkin funcionam. Praticamente enviado simultaneamente. Você pode me ajudar a escolher a resposta 'melhor', se houver uma?
epologee
2
Eu gosto mais desta resposta porque parece mais "cacau" -y (?). Os sel_registerName()olhares thingy obscurecer e do tipo que você não deve ser chamado diretamente a menos que você saiba o que está fazendo, um bocado como obj_msg_send ();)
Nicolas Miari
15
Não tenho certeza se é o Xcode 5, mas estou recebendo um aviso diferente com esta implementação: "PerformSelector pode causar um vazamento porque seu seletor é desconhecido" .
Hampden123
1
@ Hampden123: essa é uma questão diferente. veja aqui: stackoverflow.com/questions/7017281/…
sergio
52

Eu acho que isso ocorre porque, por algum motivo estranho, o seletor não está registrado no tempo de execução.

Tente registrar o seletor via sel_registerName():

SEL setErrorSelector = sel_registerName("setError:");

if([self respondsToSelector:setErrorSelector]) {
   [self performSelector:setErrorSelector withObject:[NSError errorWithDomain:@"SomeDomain" code:1 userInfo:nil]];
}
Jacob Relkin
fonte
Olá @jacobrelkin, as respostas da sua e da @ sergio funcionam. Praticamente enviado simultaneamente. Você pode me ajudar a escolher a resposta 'melhor', se houver uma?
epologee
2
@epologee NSSelectorFromStringliga de sel_registerName()qualquer maneira. Escolha o que melhor lhe convier.
Jacob Relkin
1
@epologee Eu acho que ligar sel_registerName()diretamente é mais explícito sobre o motivo de você estar fazendo isso. NSSelectorFromStringnão diz que ele tentará registrar o seletor.
Jacob Relkin
8
Não tenho certeza se é o Xcode 5, mas estou recebendo um aviso diferente com esta implementação: "PerformSelector pode causar um vazamento porque seu seletor é desconhecido" .
precisa saber é o seguinte
@ Max_Power89 Não. Veja meus outros comentários abaixo. Eu não queria gastar muito tempo nisso, então simplesmente incluí os arquivos de cabeçalho.
Hampden123
7

Eu recebi essa mensagem # # incluindo o arquivo com o método Nada mais foi usado nesse arquivo.

Mark Patterson
fonte
Embora essa seja uma solução menos elegante, ela funciona para mim, pois tenho os "suspeitos conhecidos" que podem estar recebendo o seletor. Além disso, se eu implementar a abordagem do seletor de tempo de execução, ainda recebo um aviso diferente na instrução performSelector; ou seja, "PerformSelector pode causar um vazamento porque seu seletor é desconhecido" . Então obrigado!
Hampden123
2
Nenhuma das respostas mais votadas está correta. A intenção do aviso "seletor não declarado" é capturar erros no tempo de compilação se você alterar o nome do seletor em que estava confiando. Portanto, é mais correto # importar o arquivo que declara o método em que você estava confiando.
Brane
7

Sei que estou um pouco atrasado para este segmento, mas, para ser completo, você pode desativar globalmente esse aviso usando as configurações de criação de destino.

Na seção 'Avisos do Apple LLVM - Objective-C', altere:

Undeclared Selector - NO
Quixiote
fonte
6

Se sua classe implementar o método setError: (mesmo declarando dinâmico o configurador da propriedade de erro eventual), você poderá declará-lo em seu arquivo de interface (.h) ou se não gostar de mostrá-lo dessa maneira tente com o truque complicado PrivateMethods:

@interface Yourclass (PrivateMethods)

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

@end

imediatamente antes da sua implementação, isso deve ocultar os avisos;).

i_mush
fonte
Obrigado, mas estou chamando o método de uma categoria, portanto isso não se aplica. Saúde, EP.
epologee
E alguns de nós estão fazendo coisas mais exóticas - o seletor é implementado em um objeto F #, no meu caso.
James Moore
1
Isso não elimina o aviso no XCode 7.1.1 / iOS 9.1, eu posso verPerformSelector may cause a leak because its selector is unknown
loretoparisi
3

A macro muito confortável para colocar em seu .pchou Common.hou onde quer que você quer:

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

É uma edição desta pergunta para um problema semelhante ...

Aviel Gross
fonte
3

Você pode desativá-lo no Xcode, como na captura de tela:

insira a descrição da imagem aqui

Olá Mundo
fonte
Agradável. Ainda assim, prefiro desativar o aviso apenas para casos explícitos, dizendo "clang está errado nesta ocasião, eu sei o que estou fazendo". Obrigado pela sua contribuição!
epologee
2

Você também pode converter o objeto em questão em um ID primeiro para evitar o aviso:

if ([object respondsToSelector:@selector(myMethod)]) {
    [(id)object myMethod];
}
Vigarista
fonte
1
Isso não elimina o mesmo aviso no conteúdo da expressão if, até o XC7.1 até hoje.
Martin-Gilles Lavoie
2

Outra maneira de evitar esse aviso é garantir que o método seletor fique assim:

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

Não se esqueça de "(id) remetente" se você deseja aceitar qualquer remetente ou, se preferir, especifique um tipo de objeto remetente.

Repouso
fonte
0

Enquanto a resposta correta provavelmente está em informar o Xcode através de importações ou registrar o seletor de que esse seletor existe, no meu caso, estava faltando um ponto e vírgula. Antes de "corrigir" o erro, verifique se o erro está correto e seu código não. Encontrei o erro no exemplo MVCNetworking da Apple, por exemplo.

Louis St-Amour
fonte
Não, a resposta correta não foi informar o Xcode através de importações, porque essas importações estavam em vigor. A resposta correta foi a resposta acima, marcada como ... a resposta correta, embora a resposta de @ sergio também resolvesse o problema. O uso do seletor errado não é o assunto desta pergunta, portanto, alterar o seletor não é uma resposta. Eu vou te salvar no voto negativo.
epologee 23/10
1
Obrigado por me lembrar que eu provavelmente deveria ter usado um comentário. Tudo o que posso dizer é que as importações ausentes também causam esse aviso do Xcode, se não essa instância específica. Eu recomendaria apenas o NSSelectorFromString ou outras opções de "registro" ao criar um seletor em tempo de execução ou responder a chamadas de método de maneira dinâmica (por exemplo, methodSignatureForSelector). Registrá-lo significa que você está "solucionando o erro" e, portanto, não está correto em algumas circunstâncias, porque uma abordagem mais correta seria corrigir o aviso (se a análise de clang estava correta, isso é).
Louis St-Amour
De fato, agora vejo que a pergunta original diz claramente "sem a necessidade de um protocolo implementado" - e não menciona as importações. Então, eu diria que importar a categoria em si pode ser a melhor opção para esse usuário. Qualquer outra coisa aqui pode definir o seletor duas vezes, tecnicamente falando. Sim? - Edit: Ah, eu levei isso longe demais. Obrigado pela sua resposta, vou parar agora. :)
Louis St-Amour
-1

Consegui que o aviso fosse embora adicionando outro método (divulgação: não pensei nisso, mas o encontrei pesquisando no timer programado com intervalo de tempo)

    [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
{

}

Embora eu aprecie saber como ocultar o aviso, corrigi-lo é melhor e nem as técnicas de Sergio nem Relkin funcionaram para mim, por razões desconhecidas.

user938797
fonte
1
Se alguém ler esta solução, que funcionará , ele ficará bastante confuso, incluindo o seu futuro. Se você tiver certeza de que sabe o que está fazendo, chamando um seletor inexistente, causando um aviso, pule o esboço enganoso do método e verifique se o código expressa sua intenção.
epologee
1
Bom ponto. Eu estava trabalhando com código herdado e apenas tentando descobrir como fazer o aviso desaparecer, sem tentar resolver a questão básica de por que ter um seletor inexistente. Um passo de cada vez, eu sempre digo.
user938797