【发布时间】:2010-12-02 08:42:25
【问题描述】:
我对 Objective-C 还是有点陌生,我想知道以下两个语句之间有什么区别?
[object performSelector:@selector(doSomething)];
[object doSomething];
【问题讨论】:
标签: objective-c selector dynamic-languages method-dispatch
我对 Objective-C 还是有点陌生,我想知道以下两个语句之间有什么区别?
[object performSelector:@selector(doSomething)];
[object doSomething];
【问题讨论】:
标签: objective-c selector dynamic-languages method-dispatch
基本上 performSelector 允许您动态确定在给定对象上调用哪个选择器。换句话说,选择器不需要在运行前确定。
因此,即使它们是等价的:
[anObject aMethod];
[anObject performSelector:@selector(aMethod)];
第二种形式允许您这样做:
SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];
在您发送消息之前。
【讨论】:
performSelector: 可能只有在你的类中实现目标动作时才会这样做。 performSelectorInBackground:withObject: 和 performSelectorOnMainThread:withObject:waitUntilDone: 兄弟姐妹通常更有用。用于生成后台线程,并将结果从所述后台线程回调到主线程。
performSelector 对于抑制编译警告也很有用。如果您知道该方法存在(例如在使用respondsToSelector 之后),它将阻止Xcode 说“可能无法响应your_selector”。只是不要使用它而不是找出警告的真正原因。 ;)
@ennukiller 是正确的。基本上,当您不知道(通常不可能)知道编译代码时将调用的方法的名称时,动态生成的选择器很有用。
一个关键的区别是-performSelector: 和朋友(包括multi-threaded and delayed variants)在某种程度上受到限制,因为它们被设计用于具有 0-2 个参数的方法。例如,使用 6 个参数调用 -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation: 并返回 NSString 非常笨拙,并且提供的方法不支持。
【讨论】:
NSInvocation 对象。
performSelector: 和朋友都接受对象参数,这意味着你不能使用它们来调用(例如)setAlphaValue:,因为它的参数是一个浮点数。
选择器有点像其他语言中的函数指针。当您在编译时不知道要在运行时调用哪个方法时,可以使用它们。此外,与函数指针一样,它们仅封装了调用的动词部分。如果方法有参数,你也需要传递它们。
NSInvocation 具有类似的目的,只是它将更多信息绑定在一起。它不仅包括动词部分,还包括目标对象和参数。当您想用特定参数调用特定对象的方法时,这很有用,不是现在而是将来。您可以构建一个合适的NSInvocation 并在以后触发它。
【讨论】:
两者之间还有另一个细微差别。
[object doSomething]; // is executed right away
[object performSelector:@selector(doSomething)]; // gets executed at the next runloop
这是苹果文档的摘录
"执行选择器:withObject:afterDelay: 在下一个运行循环周期和可选的延迟期之后,在当前线程上执行指定的选择器。因为它会等到下一个运行循环周期来执行选择器,所以这些方法提供了当前执行代码的自动最小延迟。多个排队的选择器按照它们排队的顺序一个接一个地执行。”
【讨论】:
performSelector:withObject:afterDelay:,但问题和您的sn-p 使用的是performSelector:,这是一种完全不同的方法。来自它的文档:performSelector:方法相当于直接向接收者发送aSelector消息。
performSelector/performSelector:withObject/performSelector:withObject:afterDelay 的行为方式都一样,这是一个错误。
对于问题中这个非常基本的示例,
[object doSomething];
[object performSelector:@selector(doSomething)];
将发生的事情没有区别。 doSomething 将由对象同步执行。只有“doSomething”是一个非常简单的方法,它不返回任何内容,也不需要任何参数。
是不是有点复杂,比如:
(void)doSomethingWithMyAge:(NSUInteger)age;
事情会变得复杂,因为 [对象 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) 返回值。
我希望我以某种方式涵盖了这个......
【讨论】: