iOS - Как реализовать executeSelector с несколькими аргументами и с afterDelay?
Я новичок в iOS. У меня есть метод выбора следующим образом -
- (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
Есть идеи, что мне не хватает?
11 ответов
Лично я считаю, что более близким решением к вашим потребностям является использование 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];
}
Потому что нет такой вещи как [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];
Вы можете упаковать свои параметры в один объект и использовать вспомогательный метод для вызова вашего исходного метода, как предлагали Майкл и другие сейчас.
Другой вариант - 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:
Самый простой вариант - изменить ваш метод так, чтобы он принимал один параметр, содержащий оба аргумента, например 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];
- (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];
Вы можете найти все типы предоставляемых методов executeSelector: здесь:
Существует множество вариантов, но не существует версии, которая принимает несколько объектов, а также задержку. Вам нужно будет обернуть ваши аргументы в 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:
Мне не нравится 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);
Я просто выпил немного, и мне нужно было вызвать оригинальный метод. Я сделал протокол и применил к нему свой объект. Другим способом является определение метода в категории, но потребуется подавление предупреждения (#pragma clang Diagnostic игнорируется "-Wincomplete-реализация").
Простой и многократно используемый способ заключается в расширении NSObject
и реализовать
- (void)performSelector:(SEL)aSelector withObjects:(NSArray *)arguments;
что-то вроде:
- (void)performSelector:(SEL)aSelector withObjects:(NSArray *)arguments
{
NSMethodSignature *signature = [self methodSignatureForSelector: aSelector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature: signature];
[invocation setSelector: aSelector];
int index = 2; //0 and 1 reserved
for (NSObject *argument in arguments) {
[invocation setArgument: &argument atIndex: index];
index ++;
}
[invocation invokeWithTarget: self];
}
Я бы просто создал пользовательский объект, содержащий все мои параметры в качестве свойств, а затем использовал бы этот единственный объект в качестве параметра
Для одного аргумента
perform(#selector(fuctionOne(_:)), with: arg1, afterDelay: 2)
@objc func fuctionOne(_ arg1: NSUserActivity) {
// Do Something
}
Для нескольких аргументов
perform(#selector(fuctionTwo(_:_:)), with: [arg1, arg2], afterDelay: 2)
@objc func fuctionTwo(_ arg1: URL, _ arg2: [UIApplication.OpenURLOptionsKey: Any] = [:]) {
// Do Something
}