[ACCEPTED]-How to use performSelector:withObject:afterDelay: with primitive types in Cocoa?-cocoa

Accepted answer
Score: 72

Here is what I used to call something I 1 couldn't change using NSInvocation:

SEL theSelector = NSSelectorFromString(@"setOrientation:animated:");
NSInvocation *anInvocation = [NSInvocation
            invocationWithMethodSignature:
            [MPMoviePlayerController instanceMethodSignatureForSelector:theSelector]];

[anInvocation setSelector:theSelector];
[anInvocation setTarget:theMovie];
UIInterfaceOrientation val = UIInterfaceOrientationPortrait;
BOOL anim = NO;
[anInvocation setArgument:&val atIndex:2];
[anInvocation setArgument:&anim atIndex:3];

[anInvocation performSelector:@selector(invoke) withObject:nil afterDelay:1];
Score: 35

Just wrap the float, boolean, int or similar 3 in an NSNumber.

For structs, I don't know 2 of a handy solution, but you could make 1 a separate ObjC class that owns such a struct.

Score: 13

DO NOT USE THIS ANSWER. I HAVE ONLY LEFT 8 IT FOR HISTORICAL PURPOSES. SEE THE COMMENTS 7 BELOW.

There is a simple trick if it is a 6 BOOL parameter.

Pass nil for NO and self 5 for YES. nil is cast to the BOOL value of 4 NO. self is cast to the BOOL value of YES.

This 3 approach breaks down if it is anything other 2 than a BOOL parameter.

Assuming self is a 1 UIView.

//nil will be cast to NO when the selector is performed
[self performSelector:@selector(setHidden:) withObject:nil afterDelay:5.0];

//self will be cast to YES when the selector is performed
[self performSelector:@selector(setHidden:) withObject:self afterDelay:10.0];
Score: 8

Perhaps NSValue, just make sure your pointers are 2 still valid after the delay (ie. no objects 1 allocated on stack).

Score: 8

I know this is an old question but if you 3 are building iOS SDK 4+ then you can use 2 blocks to do this with very little effort 1 and make it more readable:

double delayInSeconds = 2.0;
int primitiveValue = 500;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    [self doSomethingWithPrimitive:primitiveValue];     
});
Score: 6

PerformSelector:WithObject always takes 7 an object, so in order to pass arguments 6 like int/double/float etc..... You can use 5 something like this.

//NSNumber is an object..

    [self performSelector:@selector(setUserAlphaNumber:) withObject: [NSNumber numberWithFloat: 1.0f]
    afterDelay:1.5];

    -(void) setUserAlphaNumber: (NSNumber*) number{

     [txtUsername setAlpha: [number floatValue] ];
    }

Same way you can use 4 [NSNumber numberWithInt:] etc.... and in 3 the receiving method you can convert the 2 number into your format as [number int] or 1 [number double].

Score: 5

Blocks are the way to go. You can have complex 7 parameters, type safety, and it's a lot 6 simpler and safer than most of the old answers 5 here. For example, you could just write:

[MONBlock performBlock:^{[obj setFrame:SOMETHING];} afterDelay:2];

Blocks 4 allow you to capture arbitrary parameter 3 lists, reference objects and variables.

Backing 2 Implementation (basic):

@interface MONBlock : NSObject

+ (void)performBlock:(void(^)())pBlock afterDelay:(NSTimeInterval)pDelay;

@end

@implementation MONBlock

+ (void)imp_performBlock:(void(^)())pBlock
{
 pBlock();
}

+ (void)performBlock:(void(^)())pBlock afterDelay:(NSTimeInterval)pDelay
{
  [self performSelector:@selector(imp_performBlock:)
             withObject:[pBlock copy]
             afterDelay:pDelay];
}

@end

Example:

int main(int argc, const char * argv[])
{
 @autoreleasepool {
  __block bool didPrint = false;
  int pi = 3; // close enough =p

  [MONBlock performBlock:^{NSLog(@"Hello, World! pi is %i", pi); didPrint = true;} afterDelay:2];

  while (!didPrint) {
   [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeInterval:0.1 sinceDate:NSDate.date]];
  }

  NSLog(@"(Bye, World!)");
 }
 return 0;
}

Also see 1 Michael's answer (+1) for another example.

Score: 3

I would always recomend that you use NSMutableArray 9 as the object to pass on. This is because 8 you can then pass several objects, like 7 the button pressed and other values. NSNumber, NSInteger 6 and NSString are just containers of some 5 value. Make sure that when you get the object 4 from the array that you refer to to a correct 3 container type. You need to pass on NS containers. There 2 you may test the value. Remember that containers 1 use isEqual when values are compared.

#define DELAY_TIME 5

-(void)changePlayerGameOnes:(UIButton*)sender{
    NSNumber *nextPlayer = [NSNumber numberWithInt:[gdata.currentPlayer intValue]+1 ];
    NSMutableArray *array = [[NSMutableArray alloc]initWithObjects:sender, nil];
    [array addObject:nextPlayer];
    [self performSelector:@selector(next:) withObject:array afterDelay:DELAY_TIME];
}
-(void)next:(NSMutableArray*)nextPlayer{
    if(gdata != nil){ //if game choose next player
       [self nextPlayer:[nextPlayer objectAtIndex:1] button:[nextPlayer objectAtIndex:0]];
    }
}
Score: 2

I also wanted to do this, but with a method 7 that receives a BOOL parameter. Wrapping 6 the bool value with NSNumber, FAILED TO 5 PASS THE VALUE. I have no idea why.

So I 4 ended up doing a simple hack. I put the 3 required parameter in another dummy function 2 and call that function using the performSelector, where 1 withObject = nil;

[self performSelector:@selector(dummyCaller:) withObject:nil afterDelay:5.0];

-(void)dummyCaller {

[self myFunction:YES];

}
Score: 2

I find that the quickest (but somewhat dirty) way 28 to do this is by invoking objc_msgSend directly. However, it's 27 dangerous to invoke it directly because 26 you need to read the documentation and make 25 sure that you're using the correct variant 24 for the type of return value and because 23 objc_msgSend is defined as vararg for compiler 22 convenience but is actually implemented 21 as fast assembly glue. Here's some code 20 used to call a delegate method -[delegate 19 integerDidChange:] that takes a single integer 18 argument.

#import <objc/message.h>


SEL theSelector = @selector(integerDidChange:);
if ([self.delegate respondsToSelector:theSelector])
{
    typedef void (*IntegerDidChangeFuncPtrType)(id, SEL, NSInteger);
    IntegerDidChangeFuncPtrType MyFunction = (IntegerDidChangeFuncPtrType)objc_msgSend;
    MyFunction(self.delegate, theSelector, theIntegerThatChanged);
}

This first saves the selector since 17 we're going to refer to it multiple times 16 and it would be easy to create a typo. It 15 then verifies that the delegate actually 14 responds to the selector - it might be an 13 optional protocol. It then creates a function 12 pointer type that specifies the actual signature 11 of the selector. Keep in mind that all Objective-C 10 messages have two hidden first arguments, the 9 object being messaged and the selector being 8 sent. Then we create a function pointer 7 of the appropriate type and set it to point 6 to the underlying objc_msgSend function. Keep 5 in mind that if the return value is a float 4 or struct, you need to use a different variant 3 of objc_msgSend. Finally, send the message 2 using the same machinery that Objective-C 1 uses under the sheets.

Score: 1

You Could just use NSTimer to call a selector:

[NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(yourMethod:) userInfo:nil repeats:NO]

0

Score: 1

Calling performSelector with an NSNumber 6 or other NSValue will not work. Instead 5 of using the value of the NSValue/NSNumber, it 4 will effectively cast the pointer to an int, float, or 3 whatever and use that.

But the solution is 2 simple and obvious. Create the NSInvocation 1 and call

[invocation performSelector:@selector(invoke) withObject:nil afterDelay:delay]

Score: 0

Pehaps...ok, very likely, I'm missing something, but 3 why not just create an object type, say 2 NSNumber, as a container to your non-object 1 type variable, such as CGFloat?

CGFloat myFloat = 2.0; 
NSNumber *myNumber = [NSNumber numberWithFloat:myFloat];

[self performSelector:@selector(MyCalculatorMethod:) withObject:myNumber afterDelay:5.0];

More Related questions