【问题标题】:Objective-C: How to call performSelector with a BOOL typed parameter?Objective-C:如何使用 BOOL 类型的参数调用 performSelector?
【发布时间】:2011-08-16 08:42:04
【问题描述】:

有什么方法可以在选择器中发送BOOL 吗?

[self performSelector:@selector(doSomething:) withObject:YES afterDelay:1.5];

或者我应该使用NSInvocation?有人可以写一个示例吗?

【问题讨论】:

    标签: objective-c iphone selector nsinvocation


    【解决方案1】:

    如果您无法更改目标方法签名以接受NSNumber 代替BOOL,您可以使用NSInvocation 代替performSelector

    MyTargetClass* myTargetObject;
    BOOL myBoolValue = YES; // or NO
    
    NSMethodSignature* signature = [[myTargetObject class] instanceMethodSignatureForSelector: @selector( myMethodTakingBool: )];
    NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: signature];
    [invocation setTarget: myTargetObject];
    [invocation setSelector: @selector( myMethodTakingBool: ) ];
    [invocation setArgument: &myBoolValue atIndex: 2];
    [invocation invoke];
    

    【讨论】:

    • 这是最好的答案,因为它不需要修改目标选择器。但是,您实际上应该确保用于存储 BOOL 值 (myBoolValue) 的任何变量在调用执行之前都不会超出范围,否则您可能会得到不可预知的结果。我建议使用 iVar 或静态常量。
    • @quickthyme - 不清楚你的建议是什么。 'invoke' 是一个同步调用,那么 'myBoolValue' 怎么会在调用过程中超出范围呢?
    • @quickthyme - 我相信调用调用是同步的。
    • 是的,但是您的示例虽然正确,但假定在创建调用本身的同一块中调用了“调用”。然而,在其他地方调用“invoke”的情况下(这种情况更常见),本地“myBoolValue”变量可能不再存在。因此,任何引用它的地址指针都不再可信。因此,最好将指针传递给静态变量或实例变量。你的回答很好。我只是觉得这是一个值得一提的重要细节,以防以后可以为其他人省去巨大的麻烦……
    • @quickthyme - 我明白你的意思。如果您的意图是创建一个调用对象并将其存储以供以后使用,则需要确保参数不存在于您构造 NSInvocation 的堆栈框架中。
    【解决方案2】:

    您可以使用 NSNumber 来包装 bool 类型:

    BOOL myBool = YES;
    NSNumber *passedValue = [NSNumber numberWithBool:myBool];
    [self performSelector:@selector(doSomething:) withObject:passedValue afterDelay:1.5];
    

    在选择器中,要获取布尔值,请使用:

    BOOL value = [recievedObject boolValue];
    

    【讨论】:

    • 如果您能够操纵目标选择器的签名,这很好。如果不是,你应该使用 NSInvocation。
    • 如何回答或链接到建议 NSInvocation 解决方案的答案?
    • 警告:我注意到,在某些条件下,执行 performSelector 并为 BOOL 传递 NSNumber 可能会在您稍后要求该 BOOL 属性时给您带来模棱两可的结果。它似乎持有对 NSNumber(或某个数值)的引用,而不是实际的 BOOL。
    • NSInvocation 更好
    • @TomSwift 如果您无法更改原始代码,您可以在自己的类中创建一个包装器方法,以调用原始方法。然后 performSelector 在您自己的对象实例上。
    【解决方案3】:

    最简单的方法如下:

    如果你有方法

    -(void)doSomething:(BOOL)flag
    

    并希望使用 flag=NO use 执行Selecor

    [object performSelector:@selector(doSomething:) withObject:nil];
    

    在 flag=YES 的情况下,您可以发送任何对象,例如,@YES - bool 中的数字

     [object performSelector:@selector(doSomething:) withObject:@YES];
    

    注意:不要使用 @NO !只有 nil 会在您的带有 bool 参数的方法中被解释为 NO。

    【讨论】:

    • 不知道是不是bug,我在模拟器上测试iOS12iOS 8.1flag总是等于NO,不管参数是@YES还是nil.
    • 我不认为你是正确的。对于doSomething:方法,你需要创建一个包装方法,比如doSomethingObj:(NSNumber*)flag,它调用doSomething:来完成它的工作。
    • 如果你有一个NSInteger 参数,你会看到你得到的参数是来自performSelector:withObject: 的指针值,而不是[number integerValue]。 Objc 不会为您解开 NSNumber 值。
    【解决方案4】:

    像这样在主线程中使用 dispatch_after:

    dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (_animationDuration+1) * NSEC_PER_SEC);
    dispatch_after(delayTime, dispatch_get_main_queue(), ^{
        [self completeAnimation:NO];
    });
    

    【讨论】:

    • 警告 - 如果时间很关键,请不要使用它,因为它会比预定时间晚约 10% 触发,例如如果您从现在开始将其安排为 3 秒,它通常会在 3.3 秒左右触发。如果您需要更精确的计时,请使用 performSelector 或 Timer
    【解决方案5】:

    如果你想传递一个 NO,并且只有一个 NO,你可以输入一个 0 来代替

    [self performSelector:@selector(doSomething:) withObject:0 afterDelay:1.5];
    

    但请记住,只为零工作,不使用 ARC 编译,不允许隐式转换

    【讨论】:

    • 在这种情况下,您可以使用 arc 使用 nil。 [self performSelector:@selector(doSomething:) withObject:nil afterDelay:1.5];它的行为类似于 [self doSomething:NO]; 1.5秒后被调用。 -rrh
    【解决方案6】:

    这样做的另一种方法是将 nil 传递给 NOanything else 代表 YES

    [self performSelector:@selector(doSomething:) withObject:nil];

    doSomething 的 BOOL 参数将没有值

    --

    [self performSelector:@selector(doSomething:) withObject:[NSNumber numberWithBool:YES]];

    对于 BOOL 参数的 doSomething 将具有 YES 值

    --

    [self performSelector:@selector(doSomething:) withObject:[NSNumber numberWithBool:NO]];

    对于 BOOL 参数的 doSomething 将具有 YES 值(即使传递的是 NO 的数字)

    【讨论】:

    • 这非常有效 - 一个更简洁的解决方案。为了便于阅读,我使用@pieter 建议的@(YES)YESnilNO
    【解决方案7】:

    它可能不是最优雅的,但我创建了另一个例程来使用 bool 调用例程。这使选择器的内容保持简单,无需访问即可更改原始例程。

    [由 Ash 添加] 这是一个在设置布尔属性时降低接收器 alpha 值的示例:

    - (void) setVisible:(BOOL)visible
    {
        _visible = visible;
        self.alpha = 0.0;
    }
    
    - (void) setVisibleUsingNSNumber:(NSNumber *)visible
    {
        self.visible = [visible boolValue];
    }
    

    你可以在延迟之后触发它:

    [self performSelector:@selector(setVisibleUsingNSNumber:)
               withObject:[NSNumber numberWithBool:YES]
               afterDelay:0.5];
    

    请注意,现在也可以使用 Grand Central Dispatch,在这种情况下,您可以在块内使用标准 setter 方法,绕过所有这些额外代码。但是,取消 performSelector 调用比在延迟的块执行中检测取消要容易得多。

    【讨论】:

    • 听起来很有趣 - 你能说明一下你是如何做到这一点的吗?
    • 我投票赞成,因为这是做事的正确方式。不要假装你正在重写一个方法,然后故意向它传递与其签名不匹配的数据。
    【解决方案8】:

    你不能像这样向选择器发送参数。

    您可能想看看以下答案:

    Creating a selector from a method name with parameters

    【讨论】:

      【解决方案9】:

      在另一篇文章中看到了这个技巧,但是将 nil 传递给 NO,将 self 传递给 YES。

      【讨论】:

      • 不要这样做。尽管您通过了自我,但这可能会导致(在极少数情况下,但仍然是少数情况下)为“否”!参考:stackoverflow.com/a/7851666/874522
      【解决方案10】:

      使用 Objective-C 文字可以减少冗长的表达。

      [self performSelector:@selector(doSomething:) withObject:@(YES) afterDelay:1.5];
      

      注意@(YES) i.s.o。 YES

      【讨论】:

      • 不知道为什么这被否决了。将 @() 中的布尔值转换为 NSNumber 是最快、最简洁的方法。
      【解决方案11】:

      如果您在静态中传递 BOOL 参数,那么您可以使用以下..

      ----->传递“NO”:


      [self performSelector:@selector(doSomething:) withObject:0 afterDelay:1.5];
      

      现在得到 TimeInterval 之后的结果:

      -(void)doSomething:(BOOL)isDoSomething
      {
          NSLog(isDoSomething?@"Do Something":@"Don't Do Any Thing");
      }
      

      在这里你会得到' Don't Do Any Thing ' 在日志中。


      ----->传递“是”:


      [self performSelector:@selector(doSomething:) withObject:@"1" afterDelay:1.5];
      

      现在得到 TimeInterval 之后的结果:

      -(void)doSomething:(BOOL)isDoSomething
      {
          NSLog(isDoSomething?@"Do Something":@"Don't Do Any Thing");
      }
      

      在这里你会得到' Do Something ' 在日志中。


      【讨论】:

        【解决方案12】:

        与其依赖 NSInvocation 或必须修改方法以使用 NSNumber,这可以通过创建视图来更优雅地完成,例如:

        @interface CLLocationManager(CompatibleView)
        @property (nonatomic) BOOL allowsBackgroundLocationUpdates;
        @end
        
        @implementation SampleClass
        - (void)sampleFunc:(CLLocationManager *)locationManager {
            if ([locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
                locationManager.allowsBackgroundLocationUpdates = YES;
            }
        }
        @end
        

        【讨论】:

          【解决方案13】:

          尝试将您的布尔值包装在 NSNumber 中:[NSNumber numberWithBool:myBool]

          你可以用[myNumber boolValue]取回它

          【讨论】:

          • 如果您可以访问实现,这没关系。但是,如果您尝试将其用于框架中的方法,则无用。
          • 乔纳森,请参阅下面的答案,其中提供了分步指南(和代码),以了解如何准确地完成您正在寻找的事情。使用 Cocoa Touch SDK 测试并正常工作(并且可以想象任何其他框架)。它适用于原语、枚举和结构。
          【解决方案14】:

          我是这样做的:

          [_button performSelector:@selector(setUserInteractionEnabled:) withObject:[NSString stringWithFormat:@"YES"] afterDelay:nil];
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-03-08
            • 2019-07-21
            • 1970-01-01
            • 1970-01-01
            • 2022-11-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多