Завдання-C: виклик селекторів з декількома аргументами


142

У MyClass.m я визначив

- (void) myTest: (NSString *) withAString{
    NSLog(@"hi, %@", withAString);
}

та відповідна декларація в MyClass.h. Пізніше я хочу зателефонувати

[self performSelector:@selector(mytest:withAString:) withObject: mystring];

в MyClass.m, але я отримую помилку, схожу на * Закінчення програми через невдале виключення "NSInvalidArgumentException", причина: '* - [MyClass myTest: withAtring:]: нерозпізнаний селектор відправлений до інстанції 0xe421f0'

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


4
У вашому допиті запитують про "кілька аргументів", але ви використовуєте лише один. Зараз мені цікаво, як хтось БУДЕ це зробити з декількома аргументами, крім того, щоб загортати їх у масив / вислів / що завгодно.
RonLugge

Відповіді:


137

Ваш підпис методу:

- (void) myTest:(NSString *)

withAString - це параметр (ім'я вводить в оману, схоже, воно є частиною підпису селектора).

Якщо ви викликаєте функцію таким чином:

[self performSelector:@selector(myTest:) withObject:myString];

Це спрацює.

Але, як підказали інші афіші, ви можете перейменувати метод:

- (void)myTestWithAString:(NSString*)aString;

І дзвоніть:

[self performSelector:@selector(myTestWithAString:) withObject:myString];

2
Тепер, коли я бачу, що люди отримали користь від цієї відповіді, я переглянув свою відповідь; Я б запропонував, щоб виклик був просто: - (void) testWithString: (NSString *) aString;
Ліндсі Фергюсон

313

У Objective-C підпис селектора складається з:

  1. Назва методу (у цьому випадку це було б "myTest") (обов'язково)
  2. A ':' (двокрапка) після імені методу, якщо метод має вхід.
  3. Ім'я та ":" для кожного додаткового вводу.

Селектори не знають:

  1. Типи введення
  2. Тип повернення методу.

Ось реалізація класу, де метод performMethodsViaSelectors виконує інші методи класу за допомогою селекторів:

@implementation ClassForSelectors
- (void) fooNoInputs {
    NSLog(@"Does nothing");
}
- (void) fooOneIput:(NSString*) first {
    NSLog(@"Logs %@", first);
}
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second {
    NSLog(@"Logs %@ then %@", first, second);
}
- (void) performMethodsViaSelectors {
    [self performSelector:@selector(fooNoInputs)];
    [self performSelector:@selector(fooOneInput:) withObject:@"first"];
    [self performSelector:@selector(fooFirstInput:secondInput:) withObject:@"first" withObject:@"second"];
}
@end

Метод, для якого потрібно створити селектор, має один вхід, тож ви створили для нього селектор так:

SEL myTestSelector = @selector(myTest:);

3
Хороша відповідь. Щоб трохи уточнити, у вас ім'я селектора ОБОВ'ЯЗКОВО має мати принаймні одну частину, яка може брати або не приймати параметр - якщо він є, він повинен мати двокрапку. Імена селекторів з двома або більше частинами ОБОВ'ЯЗКОВО повинні мати двокрапку після кожної частини - нелегально мати селектор форми "-useFoo: andBar: toDoSomething".
Квінн Тейлор

спасибі за це. я деякий час боровся з цим, радий за допомогу!
Джеймс Холл

як щодо вхідних параметрів цілі числа? що робити в цьому випадку?
Hoang Pham

1
Вам потрібно буде обернути ціле число в об’єкт NSNumber (див. Developer.apple.com/library/ios/#documentation/Cocoa/Reference/… ) та отримати ціле значення в тілі викликаного методу. Це може бути трохи багатослівним (і я не знайшов кращого способу подолати це), але він працює чудово.
Шейн Арні

30
+100: Це приголомшливо! Я не знав про можливість використання декількох параметрів "withObject:". Я б схвалив це сто разів, якби міг ...
FreeAsInBeer

13

@Shane Arney

performSelector:withObject:withObject:

Ви також можете згадати, що цей метод призначений лише для передачі максимум 2 аргументів, і його не можна відкласти. (Наприклад performSelector:withObject:afterDelay:).

якось дивно, що яблуко підтримує лише 2 об'єкти, які потрібно надіслати, і не робить це більш загальним.


2
Дякуємо за інформацію. Мені не вдалося затримати роботу, і тепер я знаю, чому. FYI, щоб обійти межу двох об'єктів, я передав масив і потім використав його в методі.
JScarry

7

Ваш код має дві проблеми. Один був ідентифікований і відповів, а другий - ні. Першим було те, що у вашого селектора відсутнє ім'я його параметра. Однак, навіть коли ви виправите це, рядок все ще створюватиме виняток, припускаючи, що ваш переглянутий підпис методу все ще містить більше одного аргументу. Скажімо, ваш переглянутий метод оголошено як:

-(void)myTestWithString:(NSString *)sourceString comparedTo:(NSString *)testString ;

Створення селекторів для методів, які беруть кілька аргументів, цілком справедливо (наприклад, @selector (myTestWithString: compaTo :)). Однак метод performSelector дозволяє передавати лише одне значення myTest, яке, на жаль, має більше одного параметра. Він помилиться і скаже вам, що ви не вказали достатньо значень.

Ви завжди можете переосмислити свій метод для збору колекції, оскільки це лише параметр:

-(void)myTestWithObjects:(NSDictionary *)testObjects ;

Однак є більш елегантне рішення (яке не потребує рефакторингу). Відповідь - використовувати NSInvocation, поряд з його setArgument:atIndex:та invokeметодами.

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

Удачі!


3

Ваш підпис методу не має сенсу, ви впевнені, що це не помилка друку? Мені не зрозуміло, як це навіть складати, хоча, можливо, ти отримуєш попередження про те, що ігноруєш?

Скільки параметрів, як ви очікуєте, прийме цей метод?


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

2

Подумайте, що клас слід визначати як:

- (void) myTestWithSomeString:(NSString *) astring{
    NSLog(@"hi, %s", astring);
}

У вас є лише один параметр, тому ви повинні мати лише один:

Ви можете також розглянути можливість використання% @ у своєму NSLog - це просто гарна звичка потрапляти - тоді випишете будь-який об’єкт - не лише рядки.


-1

Користувачі iOS також очікують автокапіталізації: У стандартному текстовому полі перша буква пропозиції на чутливій до регістру мові автоматично пишеться з великої літери.

Ви можете вирішити, застосовувати чи ні такі функції; не існує спеціального API для жодної з перерахованих функцій, тому надання їх є конкурентною перевагою.

В документі Apple йдеться, що для цієї функції немає доступного API, а також іншої очікуваної функції на спеціальній дошці. тож вам потрібно з’ясувати власну логіку, щоб реалізувати це.

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