【问题标题】:iOS - How to implement a performSelector with multiple arguments and with afterDelay?iOS - 如何实现具有多个参数和 afterDelay 的 performSelector?
【发布时间】: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

关于我缺少什么的任何想法?

【问题讨论】:

    标签: iphone ios selector performselector


    【解决方案1】:

    就个人而言,我认为更接近您需求的解决方案是使用 NSInvocation。

    类似下面的东西可以完成这项工作:

    indexPathdataSource 是在同一个方法中定义的两个实例变量。

    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】:

    最简单的选择是修改您的方法以采用包含两个参数的单个参数,例如 NSArrayNSDictionary(或添加第二个采用单个参数的方法,解包并调用第一个方法,然后延迟调用 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】:

            我会创建一个自定义对象,将我的所有参数作为属性,然后使用该单个对象作为参数

            【讨论】:

              猜你喜欢
              • 2012-08-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-12-01
              • 1970-01-01
              相关资源
              最近更新 更多