iOS - 如何实现具有多个参数和 afterDelay 的 performSelector?
Posted
技术标签:
【中文标题】iOS - 如何实现具有多个参数和 afterDelay 的 performSelector?【英文标题】:iOS - How to implement a performSelector with multiple arguments and with afterDelay? 【发布时间】:2012-01-16 08:46:13 【问题描述】:我是 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
关于我缺少什么的任何想法?
【问题讨论】:
【参考方案1】:就个人而言,我认为更接近您需求的解决方案是使用 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];
【讨论】:
同意。应该是正确答案。非常有用的解决方案。特别是在我的情况下,不允许更改包含多个参数的方法的签名。 这看起来是一个很好的解决方案。有没有办法从使用这种技术调用的方法中获取返回值? 你如何使用这种技术指定延迟? @death_au,而不是invoke
,请致电:[inv performSelector:@selector(invoke) withObject:nil afterDelay:1];
我必须同意这是一个很好的解决方案。祝大家编码愉快!
谈话有点晚了,但有一个问题。什么是 dropDownDelegate?【参考方案2】:
因为没有 [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];
【讨论】:
如果我删除 afterDelay 参数,我不会收到错误消息。这是否意味着 afterDelay 不允许与多个参数一起使用? 您不会收到错误消息,但我敢打赌您会在运行时收到“未找到选择器”异常(并且您尝试执行的操作不会被调用) ...试试看。 :-) 这里如何传递 Bool 类型? 使它成为一个 Objective C 风格的对象(例如“NSNumber * whatToDoNumber = [NSNumber numberWithBool: doThis];
”)并将它作为一个参数传递,@virata。
这是一个单独的问题@Raj ...请单独发布。【参考方案3】:
您可以将参数打包到一个对象中,并使用辅助方法来调用您的原始方法,正如 Michael 和其他人现在所建议的那样。
另一个选项是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:
【讨论】:
这种方法的另一个好处是你可以使用__weak
给你的假定时器一个返回自我的弱链接——这样你就不会人为地延长对象的生命周期,例如如果您的 performSelector:afterDelay: 效果有点像尾递归(尽管没有递归),那么它会解决保留周期。
是的,这应该是公认的答案。它更合适,更直接。【参考方案4】:
最简单的选择是修改您的方法以采用包含两个参数的单个参数,例如 NSArray
或 NSDictionary
(或添加第二个采用单个参数的方法,解包并调用第一个方法,然后延迟调用 second 方法)。
例如,你可以有类似的东西:
- (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];
【讨论】:
如果方法不能修改怎么办,比如说它存在于 UIKit 中或其他什么地方?不仅如此,将方法更改为使用NSDictionary
也会失去类型安全性。不理想。
@fatuhoku - 括号中的内容; “添加第二种方法,该方法采用单个参数,解包并调用第一种方法”。 不管第一种方法在哪里都有效。至于类型安全,在决定使用performSelector:
(或NSInvocation
)的那一刻就丢失了。如果这是一个问题,最好的选择可能是通过 GCD。【参考方案5】:
- (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];
【讨论】:
【参考方案6】:你可以在这里找到所有提供的 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:
【讨论】:
【参考方案7】:我不喜欢 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);
【讨论】:
不错!用“目标”替换“vc”【参考方案8】:我只是做了一些调整,需要调用原始方法。我所做的是制定一个协议并将我的对象投射到它上面。 另一种方法是在类别中定义方法,但需要抑制警告(#pragma clang diagnostic ignored "-Wincomplete-implementation")。
【讨论】:
【参考方案9】:一个简单且可重用的方法是扩展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];
【讨论】:
【参考方案10】:我会创建一个自定义对象,将我的所有参数作为属性,然后使用该单个对象作为参数
【讨论】:
以上是关于iOS - 如何实现具有多个参数和 afterDelay 的 performSelector?的主要内容,如果未能解决你的问题,请参考以下文章
如何将 Objective-C initXXX 方法绑定到具有相同类型参数的 Xamarin.iOS 构造函数?
如何将 select 语句的 IN 子句中的参数作为具有多个值的参数传递?
如何让 perl 正确传递具有多个参数和复杂文件路径(空格和符号)的命令行参数?