【问题标题】:Objective-C: Calling selectors with variable argumentsObjective-C:使用可变参数调用选择器
【发布时间】:2015-12-09 09:05:41
【问题描述】:

我面临以下问题,我已经尝试了很多。我还阅读了 Stackoverflow 中的其他问题,例如: Objective-C: Calling selectors with multiple arguments 以及关于选择器的 Cocoa 核心能力,但我正在寻找将参数变量传递给选择器的最佳方法。

-(void) runAllStatusDelegates : (SEL)selector
{
 for (NSValue *val in self.statusDelegates)
 {
  id<StatusDelegate> delegate = val;
  if ([delegate respondsToSelector:selector])
  { 
   [delegate performSelector:selector];
  }
 }
}

这个方法负责调用委托中的方法。参数是一个选择器。我的问题是选择器可以有 0 - 3 个参数,如下所示。

-(void) handleBluetoothEnabled:(BOOL)aEnabled
{
 if (aEnabled)
 {
  [self.statusDelegate bluetoothEnabled];
  if (_storedPenSerialNumber != nil && ![_storedSerialNumber isEqual:kUnknownPenID])
   {
    [self runAllStatusDelegates: @selector(penConnected : _storedSerialNumber : _storedFirmware:)];
   }
 }
 else
 {
  [self.statusDelegate bluetoothDisabled];
 }
}

-(void) handleChooseDevice:(BluetoothDeviceList*)aDevices
{
 NSLog(@"Handle Choose Device");
 [self runAllStatusDelegates: @selector(chooseDevice:aDevices:)];
}


-(void) handleDiscoveryStarted
{
 NSLog(@"Discovery Started");
 [self runAllStatusDelegates: @selector(searchingForBluetoothDevice)];
 [self.statusDelegate handleStatus:@"Searching for your digipen"];
}

此实现不起作用,因为 performSelector 无法识别选择器。

我还尝试使用 @selector(penConnected::) withObject:_storedSerialNumber 来实现它,但是我还必须使用其他参数来实现另一种方法,我不希望这样。 我是 Objective-c 的新手,所以我对所有可能性都不是很熟悉。

我的想法是将一个字符串和一个参数数组传递给 runAllStatusDelegates 并在该方法中构建选择器,但这是最好的方法还是有更方便的方法?

【问题讨论】:

  • 传递给它一个数组或字典?
  • 我很好奇你为什么使用选择器而不是显式调用函数?
  • 我在不同的控制器中使用相同的委托。我有一个带有这些委托的 HashTable,我想调用一个方法来执行所有委托。这就是我运行AllStatusDelegates 的原因。我不想通过调用不同的 runAllStatusDelegates 方法或显式调用每个控制器中的每个函数来破坏这种简单性。
  • 不,但是简单地遍历所有委托并显式调用函数会更安全吗?比必要的更复杂的 NSInvocation 需要更多的代码

标签: ios objective-c


【解决方案1】:

我个人不喜欢 NSInvocation 的复杂签名。它非常适合将一个简单的函数调用排入队列并在需要时运行它,但对于您的情况,您知道选择器,因此您实际上不需要走调用路线。如果您实际上不知道要在编译时调用的选择器,我通常会发现调用会更有用,这可能由您的 API 等决定。

所以我要做的就是将一个块传递给您的 runAllStatusDelegates 方法,该方法将对您的所有代表执行:

- (void)performSelector:(SEL)selector againstAllDelegatesWithExecutionBlock:(void (^)(id<StatusDelegate>))blockToExecute
{
    for (id<StatusDelegate> delegate in self.statusDelegates)
    {
        if ([delegate respondsToSelector:selector])
        {
            blockToExecute(delegate);
        }
    }
}

然后当你想用一个函数调用你的委托时,它看起来像这样:

[self performSelector:@selector(handleAnswerOfLifeFound)
againstAllDelegatesWithExecutionBlock:^(id<StatusDelegate> delegate){
    [delegate handleAnswerOfLifeFound];
}];

我想唯一的缺点可能是您可以更改选择器并将不同的函数传递到块中。我将如何解决这个问题是通过实际确保并非所有方法都是可选的,或者如果它们是可选的以在块内进行实际检查,这将清理签名:

- (void)callAllDelegatesWithBlock:(void (^)(id<StatusDelegate>))blockToExecute
{
    for (id<StatusDelegate> delegate in self.statusDelegates)
    {
            blockToExecute(delegate);
    }
}

然后是可选方法的实际用法:

[self callAllDelegatesWithBlock^(id<StatusDelegate> delegate){
    if([delegate respondsToSelector:@selector(handleAnswerOfLifeFound)]){
        [delegate handleAnswerOfLifeFound];
    }
}];

仍然容易出错,但至少有点整洁。

【讨论】:

    【解决方案2】:

    您可以在这种情况下使用 NSInvocation

    SEL theSelector = @selector(yourSelector:);
    NSMethodSignature *aSignature = [NSMethodSignature instanceMethodSignatureForSelector:theSelector];
    NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
    [anInvocation setSelector:theSelector];
    [anInvocation setTarget:self];
    [anInvocation setArgument:&arg1 atIndex:2];
    [anInvocation setArgument:&arg2 atIndex:3];
    [anInvocation setArgument:&arg3 atIndex:4];
    [anInvocation setArgument:&arg4 atIndex:5];
    //Add more
    

    请注意,索引 0 和 1 处的参数是为目标和选择器保留的。

    欲了解更多信息http://www.cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

    【讨论】:

      【解决方案3】:

      您可以将参数绑定到选择器

      NSDictionary *argInfo=@{@"arg1":arg1,@"arg2":arg2,...};
      objc_setAssociatedObject(self,@selector(chooseDevice:aDevices:),argInfo,OBJC_ASSOCIATION_COPY)
      [self runAllStatusDelegates: @selector(chooseDevice:aDevices:)];
      

      然后在

      -(void) runAllStatusDelegates : (SEL)selector
      {
       for (NSValue *val in self.statusDelegates)
       {
        id<StatusDelegate> delegate = val;
        if ([delegate respondsToSelector:selector])
        { 
      
         NSDictionary *argInfo=objc_getAssociatedObject(self, selector);
         //call the fun use arginfo
        }
       }
      }
      

      【讨论】:

      • 别忘了#import
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-10
      • 2011-10-28
      • 2012-03-31
      • 2011-06-26
      • 2014-08-03
      • 1970-01-01
      相关资源
      最近更新 更多