Як позбутися попередження про "незадекларований вибір"


162

Я хочу використовувати селектор в екземплярі NSObject без необхідності впровадженого протоколу. Наприклад, існує метод категорії, який повинен встановлювати властивість помилки, якщо екземпляр NSObject, на який він викликається, підтримує це. Це код, і код працює за призначенням:

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

Однак компілятор не бачить методу setError: підпис, тому він дає попередження для кожного рядка, що містить @selector(setError:)фрагмент:

Undeclared selector 'setError:'

Я не хочу оголошувати протокол, щоб позбутися цього попередження, тому що я не хочу, щоб усі класи, які можуть використовувати це, реалізували щось особливе. Просто за умовою я хочу, щоб вони мали setError:метод або властивість.

Це можливо? Як?

Ура,
ЕП



Застарений селектор викличе попередження. Доступ до селектора вже не безпечний, оскільки селектор може бути видалений на деякий час.
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 ви можете зробити те ж саме через налаштування збірки "Недекларований вибір"

194

Погляньте на NSSelectorFromString .

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

Це дозволить вам створити селектор під час виконання замість часу компіляції за допомогою @selectorключового слова, і у компілятора не буде шансу поскаржитися.


Привіт @sergio, і відповіді ваших, і @ jacobrelkin працюють. Досить багато подано одночасно. Чи допоможете ви мені вибрати кращу відповідь, якщо така є?
epologee

2
Мені подобається ця відповідь більше, тому що вона виглядає більше "какао" -y (?). Річ sel_registerName()виглядає незрозумілою і такою, яку вам не слід телефонувати безпосередньо, якщо ви не знаєте, що ви робите, щось на кшталт obj_msg_send ();)
Nicolas Miari

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

Я отримав це повідомлення, щоб піти, # включивши файл методом. Нічого іншого з цього файлу не було використано.


Хоча це менш витончене рішення, воно працює для мене, оскільки у мене є "відомі підозрювані", які можуть приймати селектора. Крім того, якщо я реалізую підхід селектора виконання, я все одно отримаю інше попередження у операторі PerformSelector; а саме "PerformSelector може спричинити витік, оскільки його селектор невідомий" . Тож дякую!
Hampden123

2
Жоден із найголосніших відповідей не правильний. Метою попередження "незадекларований селектор" є введення помилок під час компіляції, якщо ви зміните ім'я селектора, на який ви покладалися. Тому найправильніше # імпортувати файл, який оголошує метод, на який ви покладалися.
Бран

7

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

У розділі "Попередження Apple LLVM - Objective-C" змініть:

Undeclared Selector - NO

6

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

@interface Yourclass (PrivateMethods)

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

@end

безпосередньо перед вашим @ виконання, це повинно приховувати попередження;).


Дякую, але я називаю метод із категорії, тому це не стосується. Ура, ЕП.
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 через імпорт або реєстрації селектора про те, що такий селектор існує, у моєму випадку я пропустив напівкрапку. Переконайтесь, що перед тим, як "виправити" помилку, можливо, помилка є правильною, а ваш код - ні. Наприклад, я виявив помилку в прикладі MVCNetworking Apple від Apple.


Ні, правильна відповідь була не в інформуванні Xcode через імпорт, тому що цей імпорт мав місце. Правильною відповіддю була відповідь вище, яка була позначена як ... правильна відповідь, хоча відповідь @ sergio також вирішила б питання. Використання неправильного селектора не є предметом цього питання, тому зміна селектора не є відповіддю. Я врятую тобі голос.
epologee

1
Дякую, що нагадали, що я, мабуть, повинен був використати коментар. Все, що я можу сказати, - це те, що відсутній імпорт також викликає це попередження Xcode, якщо не цей конкретний екземпляр. Я б рекомендував лише NSSelectorFromString або інші подібні варіанти "реєстрації" під час створення селектора під час виконання або відповіді на дзвінки методів у динамічному режимі (наприклад, методSignatureForSelector). Зареєструвавши це означає, що ви "працюєте над помилкою", і тому за певних обставин не виправдано, тому що більш правильним підходом було б виправити попередження (якщо аналіз кланг був правильним, тобто)
Луї Сент-Амур

Насправді я зараз бачу, що в первісному питанні чітко сказано, «без необхідності впровадженого протоколу» - і взагалі не згадується про імпорт. Тому я хотів би зазначити, що імпорт самої категорії може бути найкращим варіантом для цього користувача. Все інше тут може визначити селектор двічі, технічно кажучи. Так? - Редагувати: Ах, я занадто далеко взяв це. Дякую за вашу відповідь, я зараз зупинюсь. :)
Луї Сент-Амур

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

}

Хоча я ціную, що знаю, як приховати попередження, але краще виправити це, і ні методи Серхіо, ні Релкіна не працювали для мене з невідомих причин.


1
Якщо хтось інший прочитає це рішення, яке буде спрацьовувати , він / вона буде сильно розгублений, включаючи ваше майбутнє. Якщо ви впевнені, що знаєте, що ви робите, викликаючи неіснуючий селектор, тим самим викликаючи попередження, пропустіть заглушку методу введення в оману та переконайтеся, що ваш код виражає ваші наміри.
epologee

1
Гарна думка. Я працював з успадкованим кодом і просто намагався розібратися, як змусити попередження пройти, не намагаючись вирішити основне питання, чому у нього неіснуючий селектор. Я завжди кажу один крок.
user938797
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.