views:

6732

answers:

6

Hi, The NSObject method performSelector:withObject:afterDelay: allows me to invoke a method on the object with an object argument after a certain time. It cannot be used for methods with a non-object argument (e.g. ints, floats, structs, non-object pointers, etc.).

What is the simplest way to achieve the same thing with a method with a non-object argument? I know that for regular performSelector:withObject:, the solution is to use NSInvocation (which by the way is really complicated). But I don't know how to handle the "delay" part.

Thanks,

+7  A: 

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

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

harms
Yea but that would require changing the method signature, which I cannot do.
Create a wrapper method, -delayedMethodWithArgument:(NSNumber*)arg. Have it call the original method after pulling the primitive out of the NSNumber.
James Williams
Understood. Then I'm not sure of an elegant sollution, but you could add another method which is intended as the target of your performSelector:, which unpacks the NSNumber and performs the final selector on the method you currently have in mind. Another idea you could explore is to make a category on NSObject which adds perforSelector:withInt:... (and similar).
harms
NSValue (the superclass of NSNumber) will wrap anything you give it. If you want to wrap an instance of 'struct foo' called 'bar', you'd do '[NSValue valueWithBytes: '
Jim Dovey
You can use NSValue to wrap a struct. Either way, NSNumber/NSValue is the correct solution.
Peter Hosey
+4  A: 

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

Remus Rusanu
+1  A: 

Here is what I used to call something I 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 invoke];
chris
Why not just do `[theMovie setOrientation: UIInterfaceOrientationPortrait animated:NO]`? Or do you mean that the `invoke` message is in the method you perform-after-delay?
Peter Hosey
A: 

You Could just use NSTimer to call a selector:

[NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(yourMethod:) userInfo:nil repeats:NO]
Unis
You're missing the argument. The argument to `yourMethod:` in your example is the timer itself, and you haven't passed anything for `userInfo`. Even if you did use `userInfo`, you would still have to box up the value, as harms and Remus Rusanu suggested.
Peter Hosey
A: 

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

(sorry, can't do code since the HTML menu bar isn't visible) CGFloat myFloat = 2.0; NSNumber *myNumber = [NSNumber numberWithFloat:myFloat];

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

Jim Hillhouse
A: 

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

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

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