[英]Objective-C: Calling selectors with variable arguments
我面临以下问题,我已经尝试了很多。 我还阅读了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];
}
}
}
此方法负责在委托内部调用方法。 该参数是一个选择器。 我的问题是选择器可以有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并在该方法内构建选择器,但这是最好的方法还是有更方便的方法?
我个人不喜欢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];
}
}];
仍然容易出错,但至少有点整洁。
您可以在这种情况下使用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
您可以将参数绑定到选择器
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
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.