Cel-C: Wywołanie selektorów z wieloma argumentami

142

W MyClass.m zdefiniowałem

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

oraz odpowiednią deklarację w MyClass.h. Później chcę zadzwonić

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

w MyClass.m, ale pojawia się błąd podobny do * Zakończenie aplikacji z powodu niezłapanego wyjątku „NSInvalidArgumentException”, przyczyna: „* - [MyClass myTest: withAtring:]: nierozpoznany selektor wysłany do instancji 0xe421f0”

Wypróbowałem prostszy przypadek z selektorem, który nie przyjmował żadnych argumentów, który drukował łańcuch do konsoli i działał dobrze. Co jest nie tak z kodem i jak to naprawić? Dzięki.

Stu
źródło
4
Twój post zawiera pytanie o „wiele argumentów”, ale używasz tylko jednego. Teraz jestem ciekawy, jak ktoś mógłby to zrobić z wieloma argumentami, innymi niż zawijanie ich w tablicę / dyktę / cokolwiek.
RonLugge,

Odpowiedzi:

137

Twój podpis metody to:

- (void) myTest:(NSString *)

withAString jest parametrem (nazwa jest myląca, wygląda na to, że jest częścią podpisu selektora).

Jeśli wywołasz funkcję w ten sposób:

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

To będzie działać.

Ale, jak sugerowały inne plakaty, możesz zmienić nazwę metody:

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

I zadzwoń:

[self performSelector:@selector(myTestWithAString:) withObject:myString];
Lyndsey Ferguson
źródło
2
Teraz, kiedy widzę, że ta odpowiedź odniosła korzyści, przejrzałem swoją odpowiedź; Sugerowałbym, aby wywołanie było proste: - (void) testWithString: (NSString *) aString;
Lyndsey Ferguson
313

W Objective-C podpis selektora składa się z:

  1. Nazwa metody (w tym przypadku będzie to „myTest”) (wymagane)
  2. Znak „:” (dwukropek) następujący po nazwie metody, jeśli metoda ma dane wejściowe.
  3. Nazwa i „:” dla każdego dodatkowego wejścia.

Selektory nie mają wiedzy o:

  1. Typy danych wejściowych
  2. Zwracany typ metody.

Oto implementacja klasy, w której metoda performMethodsViaSelectors wykonuje inne metody klasy za pomocą selektorów:

@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

Metoda, dla której chcesz utworzyć selektor, ma jedno wejście, więc możesz utworzyć dla niej selektor w następujący sposób:

SEL myTestSelector = @selector(myTest:);
Shane Arney
źródło
3
Dobra odpowiedź. Aby nieco wyjaśnić, nazwa selektora MUSI mieć co najmniej jedną część, która może, ale nie musi, zawierać parametr - jeśli tak, to musi mieć dwukropek. Nazwy selektorów składające się z dwóch lub więcej części MUSZĄ mieć dwukropek po KAŻDEJ części - nie jest dozwolone, aby selektor miał postać „-useFoo: andBar: toDoSomething”.
Quinn Taylor
dzięki za to. Już od jakiegoś czasu się z tym zmagałem, cieszę się z pomocy!
James Hall
co powiesz na parametry wejściowe to liczby całkowite? co zrobić w takim przypadku?
Hoang Pham
1
Będziesz musiał zawinąć liczbę całkowitą w obiekt NSNumber (patrz developer.apple.com/library/ios/#documentation/Cocoa/Reference/… ) i pobrać wartość całkowitą w treści wywoływanej metody. Może być trochę rozwlekły (i nie znalazłem lepszego sposobu na obejście tego), ale działa dobrze.
Shane Arney
30
+100: To jest niesamowite! Nie wiedziałem, że mogę używać wielu parametrów „withObject:”. Głosowałbym za to sto razy, gdybym mógł ...
FreeAsInBeer
13

@Shane Arney

performSelector:withObject:withObject:

Możesz również wspomnieć, że ta metoda służy tylko do przekazywania maksymalnie 2 argumentów i nie można jej opóźniać. (np performSelector:withObject:afterDelay:).

trochę dziwne, że Apple obsługuje tylko 2 obiekty do wysłania i nie czyni go bardziej ogólnym.

Lirik
źródło
2
Dzięki za informację. Nie mogłem zmusić opóźnienia do pracy, a teraz wiem dlaczego. FYI, aby ominąć granicę dwóch obiektów, przekazałem tablicę, a następnie użyłem jej w metodzie.
JScarry
7

Twój kod ma dwa problemy. Jeden został zidentyfikowany i odebrany, ale drugi nie. Po pierwsze, w selektorze brakowało nazwy swojego parametru. Jednak nawet jeśli to naprawisz, wiersz nadal będzie zgłaszał wyjątek, zakładając, że poprawiona sygnatura metody nadal zawiera więcej niż jeden argument. Powiedzmy, że Twoja poprawiona metoda jest zadeklarowana jako:

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

Tworzenie selektorów dla metod, które przyjmują wiele argumentów, jest całkowicie poprawne (np. @Selector (myTestWithString: compareTo :)). Jednak metoda performSelector pozwala tylko na przekazanie jednej wartości do myTest, która niestety ma więcej niż jeden parametr. Wyświetli się błąd i powie Ci, że nie podałeś wystarczającej liczby wartości.

Zawsze możesz przedefiniować swoją metodę, aby pobrać kolekcję, ponieważ jest to jedyny parametr:

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

Istnieje jednak bardziej eleganckie rozwiązanie (nie wymagające refaktoryzacji). Odpowiedzią jest użycie NSInvocation wraz z jego metodami setArgument:atIndex:i invokemetodami.

Napisałem artykuł, w tym przykład kodu , jeśli chcesz uzyskać więcej informacji. Nacisk kładziony jest na gwintowanie, ale podstawy nadal mają zastosowanie.

Powodzenia!

Zack
źródło
3

Twoja sygnatura metody nie ma sensu, czy na pewno nie jest to literówka? Nie rozumiem, jak to się w ogóle kompiluje, chociaż może otrzymujesz ostrzeżenia, które ignorujesz?

Ile parametrów powinna przyjąć ta metoda?

Rob Napier
źródło
Przepraszam, że piszesz. Wpisałem go i próbowałem uprościć, zamiast kopiować i wklejać kod, ale popełniłem błąd w trakcie. Oczekuję, że ta metoda przyjmie jeden parametr; ciąg, który chciałbym wydrukować.
Stu
2

Uważam, że klasę należy zdefiniować jako:

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

Masz tylko jeden parametr, więc powinieneś mieć tylko jeden:

Możesz również rozważyć użycie% @ w swoim dzienniku NSLog - jest to po prostu dobry nawyk - wtedy wypisze dowolny obiekt - nie tylko łańcuchy.

Grouchal
źródło
-1

Użytkownicy systemu iOS oczekują również automatycznej wielkości liter: w standardowym polu tekstowym pierwsza litera zdania w języku uwzględniającym wielkość liter jest automatycznie pisana wielką literą.

Możesz zdecydować, czy wdrożyć takie funkcje; nie ma dedykowanego interfejsu API dla żadnej z wymienionych funkcji, więc zapewnienie ich stanowi przewagę konkurencyjną.

Dokument Apple mówi, że nie ma dostępnego API dla tej funkcji i niektórych innych oczekiwanych funkcji w niestandardowej klawiaturze. więc musisz znaleźć własną logikę, aby to zaimplementować.

Kannan Prasad
źródło