Objective-C: chiamata di selettori con più argomenti


142

In MyClass.m, ho definito

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

e la dichiarazione appropriata in MyClass.h. Più tardi voglio chiamare

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

in MyClass.m ma viene visualizzato un errore simile a * Terminazione dell'app a causa di un'eccezione non rilevata 'NSInvalidArgumentException', motivo: '* - [MyClass myTest: withAtring:]: selettore non riconosciuto inviato all'istanza 0xe421f0'

Ho provato un caso più semplice con un selettore che non ha preso argomenti che hanno stampato una stringa su console e che ha funzionato bene. Cosa c'è di sbagliato nel codice e come posso ripararlo? Grazie.


4
Il tuo post chiede "argomenti multipli", ma ne usi solo uno. Ora sono curioso di sapere come qualcuno lo farebbe con più argomenti, oltre a racchiuderli in un array / dict / qualunque cosa.
RonLugge,

Risposte:


137

La firma del tuo metodo è:

- (void) myTest:(NSString *)

withAString sembra essere il parametro (il nome è fuorviante, sembra che faccia parte della firma del selettore).

Se si chiama la funzione in questo modo:

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

Funzionerà.

Ma, come hanno suggerito gli altri poster, potresti voler rinominare il metodo:

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

E chiama:

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

2
Ora che vedo che le persone hanno beneficiato di questa risposta, ho rivisto la mia risposta; Vorrei suggerire che la chiamata fosse semplicemente: - (void) testWithString: (NSString *) aString;
Lyndsey Ferguson,

313

In Objective-C, la firma di un selettore è composta da:

  1. Il nome del metodo (in questo caso sarebbe "myTest") (richiesto)
  2. A ':' (due punti) che segue il nome del metodo se il metodo ha un input.
  3. Un nome e ':' per ogni input aggiuntivo.

I selettori non sono a conoscenza di:

  1. I tipi di input
  2. Il tipo di ritorno del metodo.

Ecco un'implementazione di classe in cui il metodo performMethodsViaSelectors esegue gli altri metodi di classe tramite selettori:

@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

Il metodo per cui si desidera creare un selettore ha un singolo input, quindi è necessario creare un selettore per esso in questo modo:

SEL myTestSelector = @selector(myTest:);

3
Buona risposta. Per chiarire un po ', il nome del selettore DEVE avere almeno una parte, che può o meno accettare un parametro; in tal caso, deve avere i due punti. I nomi dei selettori con due o più parti DEVONO avere i due punti dopo OGNI parte - non è legale avere un selettore del modulo "-useFoo: andBar: toDoSomething".
Quinn Taylor,

grazie per questo. Sono stato alle prese con questo per un po ', felice per l'aiuto!
James Hall,

che dire dei parametri di input sono numeri interi? cosa fare in questo caso?
Hoang Pham,

1
Dovrai racchiudere l'intero in un oggetto NSNumber (vedi developer.apple.com/library/ios/#documentation/Cocoa/Reference/… ) e recuperare il valore intero nel corpo del metodo chiamato. Può essere un po 'prolisso (e non ho trovato un modo migliore per aggirarlo) ma funziona benissimo.
Shane Arney,

30
+100: è fantastico! Non sapevo di poter utilizzare più parametri "withObject:". Vorrei votare questo cento volte se potessi ...
FreeAsInBeer

13

@Shane Arney

performSelector:withObject:withObject:

Potresti anche menzionare che questo metodo è solo per passare un massimo di 2 argomenti e non può essere ritardato. (come performSelector:withObject:afterDelay:).

un po 'strano che apple supporti solo 2 oggetti da inviare e non lo ha reso più generico.


2
Grazie per le informazioni. Non riuscivo a far funzionare il ritardo e ora so perché. Cordiali saluti, per aggirare il limite di due oggetti, ho passato un array e poi l'ho usato nel metodo.
JScarry,

7

Il tuo codice ha due problemi. Uno è stato identificato e risposto, ma l'altro no. Il primo era che al selettore mancava il nome del suo parametro. Tuttavia, anche quando lo risolvi, la riga genererà comunque un'eccezione, supponendo che la firma del metodo modificata includa ancora più di un argomento. Supponiamo che il tuo metodo modificato sia dichiarato come:

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

La creazione di selettori per metodi che accettano più argomenti è perfettamente valida (ad es. @Selector (myTestWithString: compareTo :)). Tuttavia, il metodo performSelector ti consente di passare solo un valore a myTest, che purtroppo ha più di un parametro. Si guasterà e ti dirà che non hai fornito abbastanza valori.

Puoi sempre ridefinire il tuo metodo per prendere una raccolta in quanto è solo un parametro:

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

Tuttavia, esiste una soluzione più elegante (che non richiede refactoring). La risposta è usare NSInvocation, insieme ai suoi setArgument:atIndex:e invokemetodi.

Ho scritto un articolo, incluso un esempio di codice , se vuoi maggiori dettagli. L'attenzione si concentra sul threading, ma le basi rimangono ancora valide.

In bocca al lupo!


3

La firma del tuo metodo non ha senso, sei sicuro che non sia un refuso? Non sono chiaro come sia nemmeno compilando, anche se forse stai ricevendo avvisi che stai ignorando?

Quanti parametri ti aspetti da questo metodo?


Mi dispiace che tu stia scrivendo. L'ho digitato e ho provato a renderlo più semplice invece di copiare e incollare il mio codice ma ho fatto un errore nel processo. Mi aspetto che questo metodo prenda un parametro; la stringa che vorrei stampare.
Stu,

2

Pensa che la classe debba essere definita come:

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

Hai solo un singolo parametro, quindi dovresti avere solo un singolo:

Potresti considerare di usare% @ anche nel tuo NSLog - è solo una buona abitudine entrare - scriverà qualsiasi oggetto, non solo stringhe.


-1

Gli utenti iOS si aspettano anche l'autocapitalizzazione: in un campo di testo standard, la prima lettera di una frase in una lingua sensibile al maiuscolo / minuscolo viene automaticamente capitalizzata.

Puoi decidere se implementare o meno tali funzionalità; non esiste un'API dedicata per nessuna delle funzionalità appena elencate, quindi fornirle è un vantaggio competitivo.

Il documento Apple afferma che non è disponibile alcuna API per questa funzione e alcune altre funzionalità previste in una tastiera personalizzata. quindi è necessario scoprire la propria logica per implementare questo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.