Как избавиться от предупреждения «необъявленный селектор»

162

Я хочу использовать селектор на экземпляре NSObject без необходимости реализации протокола. Например, есть метод категории, который должен установить свойство ошибки, если экземпляр NSObject, к которому он вызывается, поддерживает его. Это код, и код работает как задумано:

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

Однако компилятор не видит никакого метода с setError: signature, поэтому он выдает предупреждение для каждой строки, содержащей @selector(setError:)фрагмент:

Undeclared selector 'setError:'

Я не хочу объявлять протокол, чтобы избавиться от этого предупреждения, потому что я не хочу, чтобы все классы могли использовать это для реализации чего-то особенного. Я просто хочу, чтобы у них был setError:метод или свойство.

Это выполнимо? Как?

Приветствия,
EP

epologee
источник
Устаревший селектор вызовет предупреждение. Доступ к селектору больше не безопасен, потому что селектор может быть удален через некоторое время.
DawnSong

Ответы:

254

Другим вариантом будет отключить предупреждение с помощью:

#pragma GCC diagnostic ignored "-Wundeclared-selector"

Вы можете поместить эту строку в файл .m, где появляется предупреждение.

Обновить:

Это также работает с LLVM:

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

... your code here ...

#pragma clang diagnostic pop
Клаас
источник
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" // Do your thing #pragma clang diagnostic pop
головокружительное
да, это так, как утверждает @dizy. (Извините за поздний ответ, но я пропустил уведомление).
Клаас
Мне нужен был Олсон#pragma clang diagnostic ignored "-Wselector"
максимум
1
@mdorseif В большинстве случаев предупреждение, которое вы должны исключить, заносится в журнал компиляции. Вы можете отключить любое предупреждение с помощью этой концепции. Рад, что вы добавили свои относительно селекторов.
Клаас
@epologee вы можете сделать то же самое с помощью настройки сборки "Undeclared Selector"
194

Посмотрите на NSSelectorFromString .

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

Это позволит вам создавать селектор во время выполнения, а не во время компиляции по @selectorключевому слову, и у компилятора не будет никаких претензий.

Серджио
источник
Привет @sergio, ответы как твои, так и @ jacobrelkin работают. В значительной степени представлены одновременно. Вы поможете мне выбрать «лучший» ответ, если он есть?
epologee
2
Мне больше нравится этот ответ, потому что он выглядит более "Какао" -y (?). В sel_registerName()Thingy выглядит затенять и вид , вы не должны называть напрямую , если вы не знаете , что вы делаете, вроде как obj_msg_send ();)
Николас Миари
15
Не уверен, что это Xcode 5, но я получаю другое предупреждение с этой реализацией: «PerformSelector может вызвать утечку, потому что его селектор неизвестен» .
Hampden123
1
@ Hampden123: это другая проблема. посмотрите здесь: stackoverflow.com/questions/7017281/…
sergio
52

Я думаю, что это потому, что по какой-то странной причине селектор не зарегистрирован во время выполнения.

Попробуйте зарегистрировать селектор через sel_registerName():

SEL setErrorSelector = sel_registerName("setError:");

if([self respondsToSelector:setErrorSelector]) {
   [self performSelector:setErrorSelector withObject:[NSError errorWithDomain:@"SomeDomain" code:1 userInfo:nil]];
}
Джейкоб Релкин
источник
Привет @jacobrelkin, ответы как твоего, так и @ sergio работают. В значительной степени представлены одновременно. Вы поможете мне выбрать «лучший» ответ, если он есть?
epologee
2
@epologee все равно NSSelectorFromStringзвонит sel_registerName()под капот. Выберите тот, который подходит вам лучше.
Джейкоб Релкин
1
@epologee Я думаю, что звонки sel_registerName()напрямую объясняют, почему вы это делаете. NSSelectorFromStringне говорит вам, что попытается зарегистрировать селектор.
Джейкоб Релкин
8
Не уверен, что это Xcode 5, но я получаю другое предупреждение с этой реализацией: «PerformSelector может вызвать утечку, потому что его селектор неизвестен» .
Hampden123
@ Max_Power89 Нет. Смотрите мои другие комментарии ниже. Я не хотел тратить на это слишком много времени, поэтому просто включил заголовочные файлы.
Hampden123
7

Я получил это сообщение, чтобы уйти # включив файл методом. Из этого файла больше ничего не использовалось.

Марк Паттерсон
источник
Хотя это менее изящное решение, оно работает для меня, поскольку у меня есть «известные подозреваемые», которые могут получать селектор. Кроме того, если я реализую подход селектора времени выполнения, я все равно получу другое предупреждение в операторе executeSelector; а именно, «PerformSelector может вызвать утечку, потому что его селектор неизвестен» . Так что спасибо!
Hampden123
2
Ни один из самых популярных ответов не является правильным. Цель предупреждения «undeclared selector» состоит в том, чтобы перехватывать ошибки во время компиляции, если вы измените имя селектора, на который вы полагались. Поэтому наиболее правильно #import файла, который объявляет метод, на который вы полагались.
Брэйн
7

Я понимаю, что немного опаздываю к этой теме, но для полноты вы можете отключить это предупреждение глобально, используя настройки целевой сборки.

В разделе «Предупреждения Apple LLVM - Objective-C» изменить:

Undeclared Selector - NO
Quixiote
источник
6

Если ваш класс реализует метод setError: (даже объявив динамически установщик свойства возможной ошибки), вы можете объявить его в файле интерфейса (.h) или если вам не нравится показывать его таким образом, вы могли бы попробуйте хитрый трюк с PrivateMethods:

@interface Yourclass (PrivateMethods)

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

@end

как раз перед вашим @implementation, это должно скрывать предупреждения;).

i_mush
источник
Спасибо, но я вызываю метод из категории, так что это не относится. Приветствия, EP.
epologee
И некоторые из нас делают вещи более экзотические - в моем случае селектор реализован в объекте F #.
Джеймс Мур
1
Это не избавляет от предупреждения в XCode 7.1.1 / iOS 9.1, я вижуPerformSelector may cause a leak because its selector is unknown
loretoparisi
3

Действительно удобный макрос для вставки в ваш .pchили Common.hили где вы хотите:

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

Это редактирование этого вопроса для аналогичного вопроса ...

Авиэль Гросс
источник
3

Вы можете отключить его в Xcode, как на скриншоте:

введите описание изображения здесь

Привет мир
источник
Хороший. Тем не менее, я предпочитаю отключать предупреждение только для явных случаев, говоря «лязг не так в этом случае, я знаю, что делаю». Спасибо за ваш вклад!
epologee
2

Вы также можете сначала привести рассматриваемый объект к идентификатору, чтобы избежать предупреждения:

if ([object respondsToSelector:@selector(myMethod)]) {
    [(id)object myMethod];
}
аферист
источник
1
Это не избавляет от того же предупреждения о содержании выражения if, вплоть до XC7.1 и по сей день.
Мартин-Жиль Лавуа
2

Другой способ избежать этого предупреждения - убедиться, что ваш метод выбора выглядит так:

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

Не забудьте "(id) отправителя", если вы хотите принять любого отправителя или указать тип объекта отправителя, если вы предпочитаете.

Преставление
источник
0

Хотя правильный ответ, вероятно, заключается в информировании XCode посредством импорта или регистрации селектора о том, что такой селектор существует, в моем случае мне не хватало точки с запятой. Прежде чем «исправить» ошибку, убедитесь, что, возможно, ошибка правильная, а ваш код - нет. Например, я обнаружил ошибку в образце Apple MVCNetworking.

Луи Сен-Амур
источник
Нет, правильный ответ был не в информировании XCode через импорт, потому что этот импорт был на месте. Правильный ответ был ответом выше, который был помечен как ... правильный ответ, хотя ответ @ sergio также решил проблему. Использование неправильного селектора не является предметом этого вопроса, поэтому изменение селектора не является ответом. Я спасу тебя, хотя и понизил.
epologee
1
Спасибо, что напомнили мне, что я, вероятно, должен был использовать комментарий. Все, что я могу сказать, - то, что пропущенные импорты также вызывают это предупреждение Xcode, если не этот конкретный экземпляр Я бы рекомендовал только NSSelectorFromString или другие подобные параметры «регистрации» при создании селектора во время выполнения или динамическом ответе на вызовы метода (например, methodSignatureForSelector). Регистрация означает, что вы «работаете над ошибкой» и, следовательно, не корректны для некоторых обстоятельств, потому что более правильным подходом было бы исправить предупреждение (если анализ лязга был правильным, то есть)
Луи Сент-Амур
Фактически, теперь я вижу, что в первоначальном вопросе четко говорится «без необходимости в реализованном протоколе» - и вообще не упоминается импорт. Поэтому я бы сказал, что импорт самой категории может быть лучшим вариантом для этого пользователя. С технической точки зрения, все остальное здесь может определить селектор дважды. Да? - Правка: Ах, я зашел слишком далеко. Спасибо за ваш ответ, я сейчас остановлюсь. :)
Луи Сент-Амур
-1

Мне удалось убрать предупреждение, добавив ничтожный метод (раскрытие: я не задумывался об этом, но нашел его, прибегнув к поиску по расписанию по расписанию с интервалом времени)

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

}

Хотя я ценю знание того, как скрыть предупреждение, лучше исправить его, и ни методы Серхио, ни Релкина не помогли мне по неизвестным причинам.

user938797
источник
1
Если кто-то еще прочитает это решение, которое сработает , он / она будет очень смущен, в том числе и в будущем. Если вы уверены, что знаете, что делаете, вызывая несуществующий селектор, вызывая тем самым предупреждение, пропустите вводящую в заблуждение заглушку метода и убедитесь, что ваш код выражает ваши намерения.
epologee
1
Хорошая точка зрения. Я работал с унаследованным кодом и просто пытался выяснить, как убрать предупреждение, не пытаясь решить основной вопрос, почему существует несуществующий селектор. Я всегда говорю, шаг за шагом.
user938797