使用-performSelector:与仅调用方法


Answers:


191

基本上,performSelector允许您动态确定要在给定对象上调用选择器的选择器。换句话说,不需要在运行时确定选择器。

因此,即使这些是等效的:

[anObject aMethod]; 
[anObject performSelector:@selector(aMethod)]; 

第二种形式允许您执行以下操作:

SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];

在发送消息之前。


3
值得指出的是,您实际上会将findTheAppropriateSelectorForTheCurrentSituation()的结果分配给aSelector,然后调用[anObject performSelector:aSelector]。@selector产生SEL。
丹尼尔·扬科斯基2009年

4
performSelector:只有在类中实现target-action时,才可能使用using 。兄弟姐妹performSelectorInBackground:withObject:和兄弟姐妹performSelectorOnMainThread:withObject:waitUntilDone:通常更有用。用于产生后台线程,以及用于将结果从所述后台线程回调到主线程。
PeyloW

2
performSelector在抑制编译警告时也很有用。如果您知道该方法存在(例如使用之后respondsToSelector),它将阻止Xcode说“可能不响应your_selector”。只是不要使用它而不是找出警告的真正原因。;)
Marc

我在StackOverflow上的其他线程上读到,performSelector的使用反映了一个糟糕的设计,并且对此表示赞许。我希望我能再次找到它。我搜索了google,将结果限制为stackoverflow,并得到18,000个结果。真是的
Logicsaurus Rex 2015年

“反映出可怕的设计”过于简单。这就是我们在块可用之前所拥有的,那时或现在并不是所有使用都不好。虽然现在块可用的,这可能是新的代码,除非你正在做的事情很简单的一个更好的选择。
伊桑(Ethan)2016年

16

对于这个非常基本的例子,

[object doSomething];
[object performSelector:@selector(doSomething)]; 

发生的事情没有任何区别。doSomething将由对象同步执行。只有“ doSomething”是一个非常简单的方法,它不返回任何内容,并且不需要任何参数。

是否有些复杂,例如:

(void)doSomethingWithMyAge:(NSUInteger)age;

事情会变得复杂,因为[object doSomethingWithMyAge:42];

不能再使用“ performSelector”的任何变体调用,因为带有参数的所有变体仅接受对象参数。

这里的选择器将是“ doSomethingWithMyAge:”,但是任何尝试

[object performSelector:@selector(doSomethingWithMyAge:) withObject:42];  

根本不会编译。传递NSNumber:@(42)而不是42也是没有帮助的,因为该方法需要基本的C类型-不是对象。

另外,最多有2个参数有performSelector变体,仅此而已。虽然方法多次具有更多的参数。

我发现,虽然performSelector是同步变体:

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

总是返回一个对象,我也能够返回一个简单的BOOL或NSUInteger,并且它起作用了。

performSelector的两个主要用途之一是动态组成要执行的方法的名称,如上一个答案中所述。例如

 SEL method = NSSelectorFromString([NSString stringWithFormat:@"doSomethingWithMy%@:", @"Age");
[object performSelector:method];

另一个用途是异步向对象分发消息,该消息将稍后在当前运行循环上执行。为此,还有其他几个performSelector变体。

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
- (void)performSelector:(SEL)aSelector target:(id)target argument:(id)arg order:(NSUInteger)order modes:(NSArray *)modes;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;

(是的,我从几个基础类类别中收集了它们,例如NSThread,NSRunLoop和NSObject)

每个变体都有其自己的特殊行为,但是所有变体都具有共同点(至少在waitUntilDone设置为NO时)。“ performSelector”调用将立即返回,并且发送给对象的消息将在一段时间后才会放入当前的运行循环中。

由于执行延迟-选择器的方法自然没有返回值可用,因此所有这些异步变量中的-(void)返回值。

我希望我能以某种方式涵盖这个...


12

@ennuikiller现场。基本上,动态生成的选择器对于在编译代码时不知道(通常不知道)将要调用的方法的名称很有用。

一个主要的区别是-performSelector:和朋友(包括多线程和延迟的变体)在某种程度上受到限制,因为它们被设计为与0-2参数的方法一起使用。例如,-outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:使用6个参数进行调用并返回,NSString这非常笨拙,并且所提供的方法不支持。


5
为此,您需要使用一个NSInvocation对象。
Dave DeLong

6
另一个区别:performSelector:和朋友都setAlphaValue:带有对象参数,这意味着您不能使用它们来调用(例如),因为其参数是浮点数。
Chuck

4

选择器有点像其他语言中的函数指针。当您在编译时不知道要在运行时调用哪种方法时,可以使用它们。同样,像函数指针一样,它们仅封装调用的动词部分。如果该方法具有参数,则还需要传递它们。

An NSInvocation具有类似目的,只是将更多信息绑定在一起。它不仅包括动词部分,还包括目标对象和参数。当您想立即而不是在将来使用特定参数在特定对象上调用方法时,此功能很有用。您可以构建一个适当的文件NSInvocation并稍后将其解雇。


5
选择器实际上根本不像函数指针,因为函数指针是可以使用参数调用的东西,而选择器可用于在实现它的任何对象上调用特定方法。选择器不像函数指针那样具有完整的调用上下文。
bbum

1
选择器与函数指针不同,但我仍然认为它们是相似的。它们代表动词。C函数指针也代表动词。如果没有其他上下文,两者都不有用。选择器需要一个对象和参数。函数指针需要参数(可能包括要在其上进行操作的对象)。我的观点是强调它们与NSInvocation对象的区别,NSInvocation对象包含所有必需的上下文。也许我的比较令人困惑,在这种情况下,我表示歉意。
Daniel Yankowsky,2009年

1
选择器不是函数指针。差远了。实际上,它们是简单的C字符串,其中包含方法的“名称”(与“功能”相对)。它们甚至不是方法签名,因为它们没有嵌入参数类型。对于同一选择器(不同的参数类型或不同的返回类型),一个对象可以具有多个方法。
Motti Shneor

-7

两者之间还有另一个细微的区别。

    [object doSomething]; // is executed right away

    [object performSelector:@selector(doSomething)]; // gets executed at the next runloop

这是Apple文档的摘录

“ performSelector:withObject:afterDelay:在下一个运行循环周期和可选的延迟时间之后,在当前线程上执行指定的选择器。因为它一直等到下一个运行循环周期执行选择器,所以这些方法提供了从当前执行的代码。多个排队的选择器将按照排队的顺序依次执行。”


1
您的回答实际上是错误的。您引用的文档是关于performSelector:withObject:afterDelay:,但是问题和代码段都在使用performSelector:,这是一种完全不同的方法。从文档中获取:<quote>该performSelector:方法等效于aSelector直接向接收方发送消息。</ quote>
jscs 2011年

3
感谢Josh的澄清。你是对的; 我认为performSelector/performSelector:withObject/performSelector:withObject:afterDelay所有人的举止都一样,这是一个错误。
avi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.