ChatGPT解决这个技术问题 Extra ChatGPT

使用 -performSelector:与仅调用方法

我对 Objective-C 还是有点陌生,我想知道以下两个语句之间有什么区别?

[object performSelector:@selector(doSomething)]; 

[object doSomething];

b
bbum

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

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

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

第二种形式允许您这样做:

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

在您发送消息之前。


值得指出的是,您实际上会将 findTheAppropriateSelectorForTheCurrentSituation() 的结果分配给 aSelector,然后调用 [anObject performSelector:aSelector]。 @selector 产生一个 SEL。
只有在类中实现 target-action 时才可能使用 performSelector:。兄弟 performSelectorInBackground:withObject:performSelectorOnMainThread:withObject:waitUntilDone: 通常更有用。用于生成后台线程,并将结果从所述后台线程回调到主线程。
performSelector 对于抑制编译警告也很有用。如果您知道该方法存在(例如在使用 respondsToSelector 之后),它将阻止 Xcode 说“可能不响应 your_selector”。只是不要使用它而不是找出警告的真正原因。 ;)
我在 StackOverflow 上的其他线程上读到,使用 performSelector 反映了一个可怕的设计,并且它有很多赞。我希望我能再次找到它。我搜索了谷歌,将结果限制为 stackoverflow,得到了 18,000 个结果。哇。
“可怕设计的反映”过于简单化。这是我们在块可用之前所拥有的,当时或现在并非所有用途都是不好的。尽管现在可以使用块,但这可能是新代码的更好选择,除非您正在做一些非常简单的事情。
M
Motti Shneor

对于问题中的这个非常基本的示例,

[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 类型——而不是对象。

此外,performSelector 变体最多有 2 个参数,仅此而已。虽然方法很多时候有更多的参数。

我发现虽然 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;

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

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

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

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


Q
Quinn Taylor

@ennukiller 很到位。基本上,当您不(通常不可能)知道编译代码时将调用的方法的名称时,动态生成的选择器很有用。

一个关键区别是 -performSelector: 和朋友(包括 multi-threaded and delayed variants)有些限制,因为它们被设计用于具有 0-2 个参数的方法。例如,使用 6 个参数调用 -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation: 并返回 NSString 非常笨拙,并且提供的方法不支持。


为此,您需要使用 NSInvocation 对象。
另一个区别:performSelector: 和朋友都接受对象参数,这意味着您不能使用它们来调用(例如)setAlphaValue:,因为它的参数是一个浮点数。
D
Daniel Yankowsky

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

NSInvocation 具有类似的目的,只是它将更多信息绑定在一起。它不仅包括动词部分,还包括目标对象和参数。当您想用特定参数调用特定对象的方法时,这很有用,不是现在而是将来。您可以构建一个适当的 NSInvocation 并在以后触发它。


选择器实际上根本不像函数指针,因为函数指针是您可以使用参数调用的东西,并且选择器可用于调用实现它的任何对象上的特定方法。选择器不像函数指针那样具有完整的调用上下文。
选择器与函数指针不同,但我仍然认为它们是相似的。它们代表动词。 C 函数指针也代表动词。如果没有额外的上下文,两者都没有用。选择器需要一个对象和参数;函数指针需要参数(可能包括要操作的对象)。我的意思是强调它们与 NSInvocation 对象的不同之处,后者确实包含所有必要的上下文。也许我的比较令人困惑,在这种情况下,我道歉。
选择器不是函数指针。差远了。它们实际上是简单的 C 字符串,包含方法的“名称”(与“函数”相反)。它们甚至不是方法签名,因为它们没有嵌入参数的类型。对于同一个选择器(不同的参数类型或不同的返回类型),一个对象可以有多个方法。
a
avi

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

    [object doSomething]; // is executed right away

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

这是Apple文档的摘录

“performSelector:withObject:afterDelay:在下一个运行循环周期和一个可选的延迟周期之后在当前线程上执行指定的选择器。因为它等到下一个运行循环周期执行选择器,这些方法提供了一个自动的迷你延迟当前执行的代码。多个排队的选择器按照它们排队的顺序一个接一个地执行。


你的回答实际上是不正确的。您引用的文档是关于 performSelector:withObject:afterDelay:,但问题和您的代码段使用的是 performSelector:,这是一种完全不同的方法。从它的文档中:<quote>performSelector: 方法相当于直接向接收者发送 aSelector 消息。</quote>
感谢乔希的澄清。你是对的;我认为 performSelector/performSelector:withObject/performSelector:withObject:afterDelay 的行为方式都相同,这是一个错误。