2011-12-08 6 views
86

Jestem nowicjuszem iOS. Mam metodę wyboru następująco -iOS - Jak zaimplementować performSelector z wieloma argumentami iz afterDelay?

- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second 
{ 

} 

Próbuję zaimplementować coś takiego -

[self performSelector:@selector(fooFirstInput:secondInput:) withObject:@"first" withObject:@"second" afterDelay:15.0]; 

Ale to daje mi błąd mówiąc -

Instance method -performSelector:withObject:withObject:afterDelay: not found 

pomysłów co do czego mi brakuje?

Odpowiedz

131

Osobiście uważam, że bliższym rozwiązaniem Twoich potrzeb jest korzystanie z NSInvocation.

coś jak następuje zrobi pracę:

indexPath i DataSource są dwie zmienne instancji zdefiniowane w taki sam sposób.

SEL aSelector = NSSelectorFromString(@"dropDownSelectedRow:withDataSource:"); 

if([dropDownDelegate respondsToSelector:aSelector]) { 
    NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[dropDownDelegate methodSignatureForSelector:aSelector]]; 
    [inv setSelector:aSelector]; 
    [inv setTarget:dropDownDelegate]; 

    [inv setArgument:&(indexPath) atIndex:2]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation 
    [inv setArgument:&(dataSource) atIndex:3]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation 

    [inv invoke]; 
} 
+1

tak. to powinna być poprawna odpowiedź. – hyperspasm

+2

Uzgodnione. To powinna być poprawna odpowiedź. Bardzo pomocne rozwiązanie. Zwłaszcza w moim przypadku, gdy nie można zmienić sygnatury metody zawierającej wiele argumentów. – AbhijeetMishra

+0

To wygląda na świetne rozwiązanie. Czy istnieje sposób na uzyskanie wartości zwracanej z metody wywoływanej za pomocą tej techniki? –

94

Ponieważ nie ma czegoś takiego jak metoda [NSObject performSelector:withObject:withObject:afterDelay:].

Musisz hermetyzować dane, które chcesz przesłać, w jednym obiekcie typu Objective C (np. NSArray, NSDictionary, jakiś niestandardowy typ C celu), a następnie przepuścić go przez dobrze znaną i kochaną metodę [NSObject performSelector:withObject:afterDelay:].

Na przykład:

NSArray * arrayOfThingsIWantToPassAlong = 
    [NSArray arrayWithObjects: @"first", @"second", nil]; 

[self performSelector:@selector(fooFirstInput:) 
      withObject:arrayOfThingsIWantToPassAlong 
      afterDelay:15.0]; 
+0

Nie otrzymam błędu, jeśli usunę parametr afterDelay. Czy to oznacza, że ​​poDelay nie może być używany z więcej niż jednym parametrem? – Suchi

+1

nie otrzymasz błędu, ale założę się, że w czasie wykonywania uzyskasz wyjątek "nie znaleziono selektora" (a rzecz, którą próbujesz wykonać, nie zostanie wywołana) ... spróbuj i zobacz . :-) –

+0

Jak mogę przekazać typ Bool tutaj? – virata

6
- (void) callFooWithArray: (NSArray *) inputArray 
{ 
    [self fooFirstInput: [inputArray objectAtIndex:0] secondInput: [inputArray objectAtIndex:1]]; 
} 


- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second 
{ 

} 

i nazywają go:

[self performSelector:@selector(callFooWithArray) withObject:[NSArray arrayWithObjects:@"first", @"second", nil] afterDelay:15.0]; 
8

Najprostszym rozwiązaniem jest, aby zmodyfikować swój sposób podjąć pojedynczy parametr zawierający oba argumenty, takim jak NSArray lub NSDictionary (lub dodaj drugą metodę, która pobiera pojedynczy parametr, rozpakowuje ją i wywołuje pierwszą metodę, a następnie wywołaj sekundę metoda opóźnienia).

Na przykład można mieć coś takiego:

- (void) fooOneInput:(NSDictionary*) params { 
    NSString* param1 = [params objectForKey:@"firstParam"]; 
    NSString* param2 = [params objectForKey:@"secondParam"]; 
    [self fooFirstInput:param1 secondInput:param2]; 
} 

a potem to nazwać, można zrobić:

[self performSelector:@selector(fooOneInput:) 
     withObject:[NSDictionary dictionaryWithObjectsAndKeys: @"first", @"firstParam", @"second", @"secondParam", nil] 
     afterDelay:15.0]; 
+0

Co się stanie, jeśli metody nie można zmienić, powiedzieć, że ona żyje w UIKit czy coś w tym stylu? Co więcej, zmiana metody używania 'NSDictionary' również powoduje utratę bezpieczeństwa typów. Nieidealny. – fatuhoku

+0

@fatuhoku - To pokrywa się z nawiasami; "dodaj drugą metodę, która pobiera pojedynczy parametr, rozpakowuje ją i wywołuje pierwszą metodę". To działa bez względu na to, gdzie znajduje się pierwsza metoda. Jeśli chodzi o bezpieczeństwo typu, zostało to utracone w momencie podjęcia decyzji o użyciu 'performSelector:' (lub 'NSInvocation'). Jeśli to obawa, najlepszą opcją będzie prawdopodobnie przejście przez GCD. – aroth

5

można znaleźć wszystkie rodzaje świadczonych performSelector: Metody tutaj:

http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html

Istnieje grono wariacje, ale nie ma wersji, która zabiera wiele obiektów, a także opóźnienie. Zamiast tego musisz zawrzeć argumenty w NSArray lub NSDictionary.

- performSelector: 
- performSelector:withObject: 
- performSelector:withObject:withObject: 
– performSelector:withObject:afterDelay: 
– performSelector:withObject:afterDelay:inModes: 
– performSelectorOnMainThread:withObject:waitUntilDone: 
– performSelectorOnMainThread:withObject:waitUntilDone:modes: 
– performSelector:onThread:withObject:waitUntilDone: 
– performSelector:onThread:withObject:waitUntilDone:modes: 
– performSelectorInBackground:withObject: 
32

Można spakować swoje parametry w jeden obiekt i użyć metody pomocnika nazwać swoją oryginalną metodę jak Michael i inni teraz, sugerowali.

Inną opcją jest dispatch_after, która pobiera blok i umieszcza go w określonym czasie.

double delayInSeconds = 15.0; 
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); 

dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 

    [self fooFirstInput:first secondInput:second]; 

}); 

Albo, jak już odkryte, jeśli nie wymagają opóźnienia można po prostu użyć - performSelector:withObject:withObject:

+0

To, co jest dobre w tym podejściu, polega na tym, że możesz użyć '__weak', aby dać swojemu udawanemu zegarowi tylko słabe ogniwo z powrotem do siebie, aby nie sztucznie wydłużyć cykl życia twojego obiektu i np. jeśli twój performSelector: afterDelay: działa trochę jak rekursja ogona (choć bez rekursji), to rozwiązuje cykl zatrzymania. – Tommy

+0

To powinna być zaakceptowana odpowiedź. – user1105951

+0

Tak, to powinna być zaakceptowana odpowiedź. Jest bardziej odpowiedni i prosto do przodu. – Roohul

1

Właśnie zrobiłem jakieś swizzling i potrzebne, aby zadzwonić do oryginalnej metody. To, co zrobiłem, to protokół i rzuciłem na niego mój obiekt. Innym sposobem jest zdefiniowanie metody w kategorii, ale wymagałoby to zignorowania ostrzeżenia (diagnostyka #pragma clang została zignorowana "-Wincomplete-implementation").

2

Nie podoba mi się sposób NSInvocation, zbyt skomplikowany. Zachowajmy prostotę i czystość:

// Assume we have these variables 
id target, SEL aSelector, id parameter1, id parameter2; 

// Get the method IMP, method is a function pointer here. 
id (*method)(id, SEL, id, id) = (void *)[target methodForSelector:aSelector]; 

// IMP is just a C function, so we can call it directly. 
id returnValue = method(target, aSelector, parameter1, parameter2); 
+0

Nice! Zamień "vc" na "cel" – Anton

Powiązane problemy