【问题标题】:Using -performSelector: vs. just calling the method使用 -performSelector:与仅调用方法
【发布时间】:2010-12-02 08:42:25
【问题描述】:

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

[object performSelector:@selector(doSomething)]; 

[object doSomething];

【问题讨论】:

    标签: objective-c selector dynamic-languages method-dispatch


    【解决方案1】:

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

    因此,即使它们是等价的:

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

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

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

    在您发送消息之前。

    【讨论】:

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

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

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

    【讨论】:

    • 为此,您需要使用NSInvocation 对象。
    • 另一个区别:performSelector: 和朋友都接受对象参数,这意味着你不能使用它们来调用(例如)setAlphaValue:,因为它的参数是一个浮点数。
    【解决方案3】:

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

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

    【讨论】:

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

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

        [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 消息。
    • 感谢 Josh 的澄清。你是对的;我认为performSelector/performSelector:withObject/performSelector:withObject:afterDelay 的行为方式都一样,这是一个错误。
    【解决方案5】:

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

    [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) 返回值。

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

    【讨论】:

      猜你喜欢
      • 2012-10-06
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-11
      相关资源
      最近更新 更多