简体   繁体   English

Objective-C:调用带有可变参数的选择器

[英]Objective-C: Calling selectors with variable arguments

I'm facing the following problem and I already tried a lot. 我面临以下问题,我已经尝试了很多。 I have also read the others Questions in Stackoverflow like: Objective-C: Calling selectors with multiple arguments and the Cocoa Core Competencies about Selectors, but I'm searching for the best way to pass a variable of arguments to a selector. 我还阅读了Stackoverflow中的其他问题,例如: Objective-C:调用具有多个参数的选择器以及有关选择器的Cocoa核心能力,但是我正在寻找将参数变量传递给选择器的最佳方法。

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

This method is responsible to call the methods inside the delegates. 此方法负责在委托内部调用方法。 The parameter is a Selector. 该参数是一个选择器。 My Problem is that the selector can have 0 - 3 arguments, as shown below. 我的问题是选择器可以有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"];
}

This implementation isn't working because the performSelector is not recognizing the selector. 由于performSelector无法识别选择器,因此该实现无法正常工作。

I also tried to implement it with @selector(penConnected::) withObject:_storedSerialNumber but then I have to implement another method with additional arguments as well and I don't want that. 我也尝试使用@selector(penConnected::) withObject:_storedSerialNumber来实现它,但是然后我还必须实现另一个带有附加参数的方法,我不想要那样。 I'm new to objective-c so I'm not so familiar with all possibilities. 我是Objective-c的新手,所以我不太熟悉所有可能性。

My idea is to pass a String and an Array of arguments to runAllStatusDelegates and build up the selector inside that method, but is this the best way or are there more convenient ways? 我的想法是将字符串和参数数组传递给runAllStatusDelegates并在该方法内构建选择器,但这是最好的方法还是有更方便的方法?

I am personally not a fan of NSInvocation for complex signatures. 我个人不喜欢NSInvocation的复杂签名。 Its really great for enqueueing a simple function call on a queue and running it when you need it but for your case, you know the selector so you don't really need to go the invocation route. 对于在队列中加入一个简单的函数调用并在需要时运行它,这确实很棒,但是对于您的情况,您知道选择器,因此您实际上不需要走调用路线。 I typically find invocations are more useful if you don't actually know the selector you want to call at compile time, maybe its determined by your API etc. 我通常会发现,如果您实际上不知道要在编译时调用的选择器(可能由您的API决定),则调用会更有用。

So what I would do is simply pass a block into your runAllStatusDelegates method that will execute against all your delegates: 因此,我要做的就是简单地将一个块传递到将对所有委托执行的runAllStatusDelegates方法中:

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

Then when you want to call your delegates with a function it looks like this: 然后,当您想使用一个函数调用您的代表时,它看起来像这样:

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

I guess the only downside might be that you could change the selector and pass a different function into the block. 我猜唯一的缺点可能是您可以更改选择器,然后将另一个函数传递给该块。 How I would solve this is by actually making sure not all methods are optional, or if they are optional to make the actual check inside the block, this would clean up the signature: 我将通过实际上确保不是所有方法都是可选的来解决此问题,或者如果它们是可选的以在块内进行实际检查,则这将清除签名:

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

and then your actual usage for an optional method: 然后是您实际使用的可选方法:

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

Still error-prone but at least a bit tidier. 仍然容易出错,但至少有点整洁。

You can use NSInvocation for this case 您可以在这种情况下使用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

Note that the arguments at index 0 and 1 are reserved for target and selector. 请注意,索引0和1处的参数是为目标和选择器保留的。

For more info http://www.cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html 有关更多信息, 请访问http://www.cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

you can binding the arguments to the selector 您可以将参数绑定到选择器

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

then in the 然后在

-(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
  }
 }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM