2011-12-08 2 views
86

Я новичок iOS. У меня есть метод выбора следующим образом -iOS - Как реализовать performSelector с несколькими аргументами и с afterDelay?

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

} 

Я пытаюсь реализовать что-то вроде этого -

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

Но это дает мне ошибку говоря -

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

Все идеи что мне не хватает?

ответ

131

Лично я считаю, что более близкое решение для ваших нужд - использование NSInvocation.

что-то вроде следующего будет делать работу:

indexPath и DATASOURCE две переменные экземпляра, определенные в одном методе.

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

да. это должен быть правильный ответ. – hyperspasm

+2

Согласовано. Это должен быть правильный ответ. Очень полезное решение. Специально в моем случае, когда не разрешено изменять подпись метода, содержащего несколько аргументов. – AbhijeetMishra

+0

Это похоже на отличное решение. Есть ли способ получить возвращаемое значение из метода, вызываемого этой методикой? –

94

Потому что нет такой вещи как [NSObject performSelector:withObject:withObject:afterDelay:].

Вам необходимо инкапсулировать данные, которые вы хотите отправить, в какой-то один объект Objective C (например, NSArray, NSDictionary, некоторый пользовательский тип Objective C), а затем передать его через хорошо известный и любимый метод [NSObject performSelector:withObject:afterDelay:].

Например:

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

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

У меня нет ошибки, если я удалю параметр afterDelay. Означает ли это, что afterDelay не может использоваться с несколькими параметрами? – Suchi

+1

у вас нет ошибки, но я бы поспорил, что во время выполнения вы получите исключение «selector not found» (и то, что вы пытаетесь выполнить, не будет вызвано) ... попробуйте и посмотрите , :-) –

+0

Как мне взять здесь Bool? – virata

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


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

} 

и назвать его:

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

Самый простой вариант, чтобы изменить свой метод, чтобы взять один параметр, содержащий оба аргумента, такие как NSArray или NSDictionary (или добавьте второй метод, который принимает один параметр, распаковывает его и вызывает первый метод, а затем вызывает второй метод с задержкой).

Например, вы могли бы иметь что-то вроде:

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

А потом называть его, вы можете сделать:

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

Что делать, если метод нельзя изменить, скажем, он живет в UIKit или что-то в этом роде? Мало того, что изменение метода использования 'NSDictionary' также теряет безопасность типов. Не идеально. – fatuhoku

+0

@fatuhoku - Это покрывается скобкой; msgstr "добавить второй метод, который принимает один параметр, распаковывает его и вызывает первый метод". Это работает независимо от того, где живет первый метод. Что касается безопасности типов, это было потеряно в тот момент, когда было принято решение использовать 'performSelector:' (или 'NSInvocation'). Если это вызывает беспокойство, лучшим вариантом, вероятно, будет пройти GCD. – aroth

5

Вы можете найти все виды предоставляемых performSelector: методы здесь:

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

Есть куча но нет версии, которая принимает несколько объектов, а также задержку. Вместо этого вам нужно обернуть свои аргументы в NSArray или 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

Вы можете упаковать свои параметры в один объект и использовать вспомогательный метод для вызова оригинального метода, как Майкл, и другие сейчас предложил.

Другим вариантом является dispatch_after, который займет блок и занесет его в определенное время.

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]; 

}); 

Или, как вы уже открыли для себя, если вам не нужна задержка, вы можете просто использовать - performSelector:withObject:withObject:

+0

Что хорошо для этого подхода, так это то, что вы можете использовать '__weak', чтобы дать вашему притворствующему таймеру только слабую ссылку обратно к себе, чтобы вы не искусственно расширяли жизненный цикл вашего объекта и, например, если ваш performSelector: afterDelay: эффект немного напоминает хвостовую рекурсию (хотя и без рекурсии), тогда он разрешает цикл сохранения. – Tommy

+0

Это должен быть принятый ответ. – user1105951

+0

Да, это должен быть принятый ответ. Это более уместно и прямо. – Roohul

1

Я просто сделал некоторые swizzling и нужно вызвать оригинальный метод. То, что я сделал, - это сделать протокол и применить к нему свой объект. Другим способом является определение метода в категории, но ему необходимо будет подавить предупреждение (#pragma clang diagnostic ignored "-Wincomplete-implementation").

2

Мне не нравится путь NSInvocation, слишком сложный. Давайте оставим его простым и чистым:

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

Ницца! Замените 'vc' на 'target' – Anton