Achieve method with passed variables in #selector - objective-c

Where this:
distanceTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(applyShot:newPositionOne.x with:newPositionOne.y) userInfo:nil repeats:NO];
^^ This doesn't work.
Must equal this
[self applyShot:newPositionOne.x with:newPositionOne.y];
I basically need a delay before running this method, and it passes the variables because they'll be different by the time the method runs, so it has to remember them somehow.
However, I cannot for the life of me figure out how to pass variables in an #selector.
I've done it before with button.tag for example, but never for this.
Any help will be appreciated, thank you.
I understand I can just set global variables, but is is possible to pass them?

Ok so there a couple things that you can do. You are correct, it is difficult to pass variables into an #selector() declaration. Here are two options:
1) call a different method (defined in your class) that handles the variables. For example:
distanceTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(applyShot) userInfo:nil repeats:NO];
- (void)applyShot
{
// manipulate variables and do your logic here
}
2) Pass a dictionary into the userInfo argument of:
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
This dictionary can look like this:
NSDictionary *params = #{#"newXPosition: #(90), #""newYPosition: #(100)"};
Then just change your applyShot: method to take in an NSDictionary argument, and then parse through the dictionary there to find the relevant values and "apply the shot."

A selector is not a packaged method. A selector is just the name of a message. You can think of it as a string (it used to be implemented as a string).
The traditional way to address this is with an NSInvocation which packages up a complete method call, not just the name of the message. That's well covered by Arguments in #selector. The other way to handle it is to package your options into the userInfo and change your method to read its parameters from that.
But typically the better solution today is to use dispatch_after instead:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC),
dispatch_get_main_queue(),
^(void){
[self applyShot:newPositionOne.x with:newPositionOne.y];
});

Related

Passing an argument with an NSTimer and from a regular call to the same method

I need to pass an integer to my method startEvent:. The method is a timer's action method. I've read about how to pass data with userInfo, however I don't understand how to do that so I can still call the method as I'd normally do.
How do I pass integers from an NSTimer and from a regular call to the same method?
[NSTimer scheduledTimerWithTimeInterval:0.032f target:self selector:#selector(startEvent:) userInfo:nil repeats:NO];
[self startEvent: 0];
-(void)startEvent:(int) event {
// ...
}
Use a pass-through for the timer's action method:
- (void)startEventFromTimer:(NSTimer *)tim
{
[self startEvent:[[tim userInfo] intValue]];
}
Where your timer was created with an NSNumber for its user info object.
[NSTimer scheduledTimerWithTimeInterval:0.032f
target:self
selector:#selector(startEventFromTimer:)
userInfo:#(theIntYouWantToPass)
repeats:NO];
You are starting from a fundamental misunderstanding. Your method cannot be the timers action method unless it conforms to the documented signature.
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSTimer_Class/Reference/NSTimer.html
(void)timerFireMethod:(NSTimer *)timer
The only opportunity you have to pass additional information in is via the userinfo object, as Josh Caswells answer demonstrates.

Xcode Cocos 2D How To Use Selector With Multiple Parameters

I am trying to do something like this:
CCMenuItemImage *BuyButton = [CCMenuItemImage itemWithNormalImage:#"Buy.jpg" selectedImage:#"Buy.jpg" target:self selector:#selector(Function:cnt)];
For some reason I can't pass any parameters to the function 'Function'. I have spend a lot of time looking into this but the the only solution i have found uses object ids and i would rather not get into that. This button is in a loop so i can't just have another function called thats get parameters from elsewhere.
+ (id)itemWithNormalImage:selectedImage:target:selector: does not support selectors with parameters. If you want to perform a selector that takes arguments, you can use + (id)itemWithNormalImage:selectedImage:block: instead. In the block just run any code you want:
__weak typeof(self) weakSelf = self;
[CCMenuItemImage itemWithNormalImage:#"Buy.jpg" selectedImage:#"Buy.jpg" block:^(id sender) {
[weakSelf methodWithParameterOne:one two:two];
}];
You can not send parameters by selectors by colons.
The typical example is :
[self performSelector:#selector(myMethodWithObject:) withObject:myObject];
Which calls
- (void)myMethodWithObject:(id)object;
Similarly you need to do like above, may be as :
CCMenuItemImage *BuyButton = [CCMenuItemImage itemWithNormalImage:#"Buy.jpg" selectedImage:#"Buy.jpg" target:self selector:#selector(Function:) withObject:cnt];
You need to change the Function by:
-(void)FunctionWithCnt:(<type>)cntObject;
and then use #selector(FunctionWithCnt:)

How to call a method every x seconds in Objective-C using NSTimer?

I am using Objective-C, Xcode 4.5.1 and working on an app for the iPhone.
I have a method A in which I want to call another method B to do a series of calculations every x seconds. In method A I start playing an audio file. Method B will monitor the audio every x seconds for the duration of the audio file.
I have found NSTimer as a potential solution, but am having a hard time getting it to work/understanding it.
I simply want to call Method B every x seconds and run its calculations, but NSTimer requires me to provide several things of which I'm not sure what I'm supposed to tell it.
[NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)
target:(id) select:(SEL) userInfo:(id) repeats:(BOOL)];
It is my understanding that at NSTimeInterval I provide the interval at which I want NSTimer to operate. But, how do I tell it to run Method B?
I have looked at example code, and am currently under the impression that I provide the method at the 'select:'. But, what do I write at the 'target:'? Why would I need a target? I tried entering 'self', but Xcode tells me:
Use of undeclared identifier 'self'
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self
select:#selector(targetMethod:myVolumeMonitor()) userInfo:nil repeats:YES];
So, I figure 'self' is supposed to be a pointer to an object, but where do I want to point to?
Below is a simplification of my code:
MethodA()
{
//Start playing an audio file.
//NSTimer calling Method B, as long the audio file is playing, every x seconds.
}
MethodB()
{
//Do calculations.
}
I would be grateful if somebody could provide me with some answers/point me in the right direction! (:
Target is the recipient of the message named in select.
In Objective-C functions are not called. There are rather messages sent to objects. The Object internally refers to its symbol table and determines which of its methods is being called. That is a selector. Your selector is #selector(MethodB).
(BTW: you should start method names with lower case. "methodB" would be more appropriate here.)
This leads to the question: how to determine the object to which the message is sent? That is the target. In your case, it is simply self.
BTW: In this case the selector is expected to return void and accept an id, which is the id of the NSTimer object itself. That will come handy if you want the timer to stop firing based on some conditions according to your program logic.
Most important: Your selector is then methodB: rather than methodB.
- (void) methodA
{
//Start playing an audio file.
//NSTimer calling Method B, as long the audio file is playing, every 5 seconds.
[NSTimer scheduledTimerWithTimeInterval:5.0f
target:self selector:#selector(methodB:) userInfo:nil repeats:YES];
}
- (void) methodB:(NSTimer *)timer
{
//Do calculations.
}
try this
NSTimer *aTimer = [NSTimer timerWithTimeInterval:(x) target:self selector:#selector(timerFired:) userInfo:nil repeats:YES];
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer:aTimer forMode: NSDefaultRunLoopMode];
[popUpImageView release];
- (void)timerFired:(NSTimer*)theTimer
{
if(condition)
{
[theTimer isValid]; //recall the NSTimer
//implement your methods
}
else
{
[theTimer invalidate]; //stop the NSTimer
}
}
If you look at your code and compared to the one below
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self
select:#selector(targetMethod:myVolumeMonitor()) userInfo:nil repeats:YES];
self means that you are invoking a method in same instance of your class, in your example the method is myVolumeMonitor
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self
selector:#selector(MethodB) userInfo:nil repeats:YES];
and you are good to go though
method be should look like this
- (void)MethodB:(NSTimer*)timer {
// do something
}
Well you are trying to call an normal C method, NSTimer can't to that.
The target is the an instance of the class on which to call the selector, this selector adn not select. The selector here is a SEL type which you can create with the #selector(METHOD_NAME) function.
For example this will call the handleTimer : ever 0.1 second: (For this example the AppDelegate is used):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//SNIP, some code to setup the windos.
[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:#selector(handleTimer:) userInfo:nil repeats:YES];
return YES;
}
- (void) handleTimer:(NSTimer *)timer {
// Hanlde the timed event.
}

Passing object to method in NSTimer

This is a really dumb question. How would you pass an object to a method using an NSTimer?
I mean something like this -
I have a method in BigView.m that has a method called doSomethingWithClass:.
- (void)doSomethingWithClass:(CustomClass *)class {
NSLog(#"Something was done");
}
In another class called CustomClass, I have an NSTimer -
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:bigView selector:#selector(doSomethingWithClass:) userInfo:nil repeats:NO];
Where bigView is an instance of BigView. Now I want to pass an entire instance of CustomClass as the parameter in the method doSomethingWithClass:. How do I do it?
If you don't need to refer to the timer, use the simpler performSelector:withObject:afterDelay: method.
[bigView performSelector:#selector(doSomethingWithClass:)
withObject:customClass
afterDelay:0.5];
(To cancel it, use +cancelPreviousPerformRequestsWithTarget:….)

Proper way of passing a primitive argument with an NSTimer

I'm using a basic timer that calls this method:
- (void) refresh:(id)obj
{
if (obj == YES) doSomething;
}
I want to call this method from certain areas of my code and also from a timer
[NSTimer scheduledTimerWithTimeInterval:refreshInterval
target:self
selector:#selector(refresh:)
userInfo:nil
repeats:YES];
When I put YES as the argument for the userInfo parameter, I get an EXC_BAD_ACCESS error; why is this?
Can someone help me do this the right way so that there is no ugly casting and such?
The userInfo parameter must be an object; it is typed id. YES is a primitive, namely the value 1. In order to make sure the userInfo object does not get deallocated, the timer retains it. So, when you passed YES, NSTimer was doing [(id)YES retain]. Try that in your own code and see what happens. :-P
As the Documentation states, the selector you give the method must have the signature
- (void)timerFireMethod:(NSTimer*)theTimer
This means you can't have an NSTimer invoke just any method—not directly at least. You can make a special method with the above signature which in turn invokes any method you want though.
So, say you have a method called refresh:, and you want to call it every so often, passing it YES. You can do this like so:
// somewhere
{
[NSTimer scheduledTimerWithTimeInterval:refreshInterval
target:self
selector:#selector(invokeRefresh:)
userInfo:nil
repeats:YES];
}
- (void)invokeRefresh:(NSTimer *)timer {
[self refresh:YES];
}
- (void)refresh:(BOOL)flag {
if (flag) {
// do something
}
}
In Objective-C, a primitive type is not an object. So you can't directly pass it to an argument which expects an id, which stands for a generic object. You need to wrap it into an NSNumber object.
Use
NSTimer*timer=[NSTimer scheduledTimerWithTimeInterval:refreshInterval
target:self
selector:#selector(refresh:)
userInfo:[NSNumber numberWithBool:YES]
repeats:YES];
and
- (void) refresh:(NSTimer*)timer
{
NSNumber* shouldDoSomething=[timer userInfo];
if ([shouldDoSomething boolValue]) doSomething;
}
Don't forget to invalidate and release the timer once it's done.
By the way, you don't have to compare a BOOL (or C++ bool) against YES or true or whatever. When you write
if(a>b) { ... }
a>b evaluates to a bool, and if uses the result. What you're doing there is like
if((a>b)==YES) { ... }
which is quite strange to me. It's not that the (..) after if should contain a comparison; it should contain a bool.
As a follow-up to kperryua's answer, if you want to pass a primitive through userInfo you can box it with NSNumber or NSValue; in the case of a boolean, you'd want to use [NSNumber numberWithBool:YES], then call boolValue in the timer callback to get back the primitive.