Objective-C: How to call performSelector with a BOOL typed parameter?

31,588

Solution 1

you can use NSNumber to wrap bools types:

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

and in the selector, to get the bool value, you use:

BOOL value = [recievedObject boolValue];

Solution 2

In the case that you cannot alter the target-method signature to accept a NSNumber in place of a BOOL you can use NSInvocation instead of 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];

Solution 3

The simplest way is as follows:

If you have method

-(void)doSomething:(BOOL)flag

and want to performSelecor with flag=NO use

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

In case of flag=YES you can send any object, for example, @YES - number from bool

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

Note: don't use @NO ! Only nil will be interpreted as NO in your method with bool argument.

Solution 4

use dispatch_after in main thread like this:

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

Solution 5

if you want to pass a NO,and only a NO, you can put a zero instead

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

But remember, just work for zero, doesn't compile with ARC wich disallow implicit conversion

Share:
31,588

Related videos on Youtube

Jim
Author by

Jim

Dev

Updated on July 09, 2022

Comments

  • Jim
    Jim almost 2 years

    Is there any way to send a BOOL in selector ?

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

    Or I should use NSInvocation? Could somebody write a sample please ?

  • Jonathan.
    Jonathan. over 12 years
    This is ok if you have access to the implementation. But useless if you're trying to use it for a method in a framework.
  • Jonathan Ellis
    Jonathan Ellis about 12 years
    Jonathan, see my answer below which gives a step-by-step guide (and code) for how to do exactly what you're looking for. Test and works fine with Cocoa Touch SDK (and would imagine any other frameworks). It works with primitives, enums and structs.
  • TomSwift
    TomSwift over 11 years
    This is fine if you are able to manipulate the signature of the target selector. If not you should use NSInvocation.
  • Phil
    Phil over 11 years
    DO NOT DO THIS. This MAY result (in very few cases, but still a few cases) in a NO despite you passed self! Reference: stackoverflow.com/a/7851666/874522
  • quickthyme
    quickthyme about 11 years
    This is the best answer, as it does not require the target selector to be modified. HOWEVER, you should actually make sure that whatever variable you use to store the BOOL value (myBoolValue) does not fall out of scope before the invocation executes, or you may end up with unpredictable results. I recommend using either an iVar or a static const.
  • TomSwift
    TomSwift about 11 years
    @quickthyme - not clear on what you're suggesting is the issue. 'invoke' is a synchronous call, so how would 'myBoolValue' fall out of scope during invocation?
  • Wirsing
    Wirsing almost 11 years
    Sounds interesting - could you clarify how exactly you achieved this?
  • quickthyme
    quickthyme almost 11 years
    I could be wrong, but my understanding is that by creating the NSInvocation object, you are essentially archiving an Obj-C message that will execute outside of this block of code. The address pointer (for myBoolVal) you are passing as the argument will fall out of scope, and that memory address will no longer be valid.
  • TomSwift
    TomSwift almost 11 years
    @quickthyme - I believe the call to invoke is synchronous.
  • quickthyme
    quickthyme almost 11 years
    Yes, but your example, while correct, assumes that 'invoke' is being called within the same block that the invocation itself gets created. In situations where 'invoke' is being called somewhere else however, (which is more often), the local 'myBoolValue' variable may no longer exist. Therefore, any address pointers referencing it are no longer trustworthy. Hence, it might be better to pass a pointer to a static var or an instance var instead. Your answer is fine. I just felt that this was an important detail worth mentioning in case it could save someone else enormous trouble later on...
  • TomSwift
    TomSwift almost 11 years
    @quickthyme - I see your point. If your intent is to create an invocation object and store it for use later, you need to ensure that the arguments don't live in the stack frame where you constructed the NSInvocation.
  • Morkrom
    Morkrom over 10 years
    How bout an answer or a link to an answer suggesting an NSInvocation solution?
  • Richie Hyatt
    Richie Hyatt about 10 years
    with arc you can use nil in this case. [self performSelector:@selector(doSomething:) withObject:nil afterDelay:1.5]; and it will behave like [self doSomething:NO]; was called after 1.5 seconds. -rrh
  • Matjan
    Matjan about 10 years
    Warning: I've noticed that under certain conditions doing performSelector and passing an NSNumber for BOOL can give you ambiguous results when you later ask for that BOOL property. it seems to hold a ref to the NSNumber (or some numerical value) instead of an actual BOOL.
  • Ash
    Ash about 10 years
    I'm voting this up because it is the proper way to do things. Don't pretend you're overriding a method then deliberately pass it data that does not match its signature.
  • mylogon
    mylogon over 8 years
    Not sure why this got a down vote. Converting a boolean in a @() to a NSNumber is the quickest and neatest way to do this.
  • Jamie - Fenrir Digital Ltd
    Jamie - Fenrir Digital Ltd almost 8 years
    This works perfectly - a much neater solution. For readability I use as @pieter suggested @(YES) for YES and nil for NO
  • Startry
    Startry about 7 years
    NSInvocation is better
  • zhongwuzw
    zhongwuzw over 5 years
    I don't know wether it's a bug, I tested on iOS12 and iOS 8.1 on simulator, the flag always equal to NO, no matter parameter is @YES or nil.
  • DawnSong
    DawnSong over 5 years
    I don't think you are correct. For the doSomething: method, you need to create a wrapper method, like doSomethingObj:(NSNumber*)flag, which call doSomething: to finish its work.
  • DawnSong
    DawnSong over 5 years
    If you have a NSInteger parameter, you will see the argument you get is the pointer value from performSelector:withObject:, instead of the [number integerValue]. Objc won't unwrap the NSNumber value for you.
  • DawnSong
    DawnSong over 5 years
    @TomSwift If you cannot change the original code, you can create a wrapper method in your own class, to call the original method. Then performSelector on your own object instance.
  • QuangDT
    QuangDT over 4 years
    Warning - do not use this if timing is crucial as this will fire about 10% later than scheduled e.g. if you schedule it for 3s from now, it will typically fire at about 3.3s. If you need more exact timing use performSelector or a Timer