Objective-C: How to call performSelector with a BOOL typed parameter?
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
Related videos on Youtube
Comments
-
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. over 12 yearsThis 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 about 12 yearsJonathan, 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 over 11 yearsThis is fine if you are able to manipulate the signature of the target selector. If not you should use NSInvocation.
-
Phil over 11 yearsDO 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 about 11 yearsThis 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 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 almost 11 yearsSounds interesting - could you clarify how exactly you achieved this?
-
quickthyme almost 11 yearsI 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 almost 11 years@quickthyme - I believe the call to invoke is synchronous.
-
quickthyme almost 11 yearsYes, 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 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 over 10 yearsHow bout an answer or a link to an answer suggesting an NSInvocation solution?
-
Richie Hyatt about 10 yearswith 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 about 10 yearsWarning: 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 about 10 yearsI'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 over 8 yearsNot 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 almost 8 yearsThis works perfectly - a much neater solution. For readability I use as @pieter suggested
@(YES)
forYES
andnil
forNO
-
Startry about 7 yearsNSInvocation is better
-
zhongwuzw over 5 yearsI don't know wether it's a bug, I tested on
iOS12
andiOS 8.1
on simulator, theflag
always equal toNO
, no matter parameter is@YES
ornil
. -
DawnSong over 5 yearsI don't think you are correct. For the
doSomething:
method, you need to create a wrapper method, likedoSomethingObj:(NSNumber*)flag
, which calldoSomething:
to finish its work. -
DawnSong over 5 yearsIf you have a
NSInteger
parameter, you will see the argument you get is the pointer value fromperformSelector:withObject:
, instead of the[number integerValue]
. Objc won't unwrap the NSNumber value for you. -
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 over 4 yearsWarning - 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