Pass method as argument - objective-c

can I pass a method as an argument?
I don't succeed to pass the method targetOpenView in the example below:
-(void) targetTimeView:(id)sender {
[self TimeViewWithtimeInterval:.6 selector:targetOpenView]; //targetOpenView does NOT work
}
-(void) timeViewWithtimeInterval:(float)interval selector:openViewMethod{
[NSTimer scheduledTimerWithTimeInterval:interval target:self selector:#selector(openViewMethod) userInfo:nil repeats:NO];
}
Any suggestions how I could make this work? Thanks!

You need the #selector compiler directive to extract the select from a method name, like you did when creating the timer:
[self TimeViewWithtimeInterval:.6 selector:#selector(targetOpenView)];
And define your argument to the type SEL:
-(void) TimeViewWithtimeInterval:(float)interval selector:(SEL)openViewMethod
{
...
}
Then, when passing the argument to the NSTimer method you can leave off the #selector since the type is already a selector:
[NSTimer scheduledTimerWithTimeInterval:interval target:self
selector:#selector(openViewMethod) /* here */
userInfo:nil repeats:NO];
[NSTimer scheduledTimerWithTimeInterval:interval target:self
selector:openViewMethod /* pass it directly */
userInfo:nil repeats:NO];

Related

Why NSTimer sends positions every 5 seconds instead of every 60?

Why NSTimer sends positions every 5 seconds instead of every 60?
- (void)startTimer {
self.timer = [NSTimer scheduledTimerWithTimeInterval:60.0
target:self
selector:#selector(sendPosition)
userInfo:nil
repeats:YES];
}
- (void)stopTimer {
if(self.timer){
[self.timer invalidate];
self.timer = nil;
}
}
I suspect that there are multiple timers created due to multiple firing of startTimer function. To ensure that there is only one instance of such timer, you can implement the following.
- (void)startTimer {
// stop and remove timer first if it is already there
if(self.timer){
[self.timer invalidate];
self.timer = nil;
}
self.timer = [NSTimer scheduledTimerWithTimeInterval:60.0
target:self
selector:#selector(sendPosition)
userInfo:nil
repeats:YES];
}
This way, no matter how many times the startTimer was called,there is only one instance of it.

Checking if NSTimer was added to NSRunLoop

Let's say I'm creating NSTimer in some place in the code and later, I want to add it to the mainRunLoop only if it wasn't already added before:
NSTimer* myTimer = [NSTimer timerWithTimeInterval:1.0f
target:self
selector:#selector(targetMethod:)
userInfo:nil
repeats:YES];
Another place in the code:
if("my myTimer wasn't added to the mainRunLoop")
{
NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
[runLoop addTimer:myTimer forMode:NSDefaultRunLoopMode];
}
Is there a way to check this?
Try this:
CFRunLoopRef loopRef = [[NSRunLoop mainRunLoop] getCFRunLoop];
BOOL timerAdded = CFRunLoopContainsTimer(loopRef, (CFRunLoopTimerRef)myTimer ,kCFRunLoopDefaultMode);
then check timerAdded variable
Yes; keep a reference to it in an instance variable and check for non-nil:
#interface MyClass() {
NSTimer *_myTimer;
}
#end
...
if (!_myTimer)
{
_myTimer = [NSTimer timerWithTimeInterval:1.0f
target:self
selector:#selector(targetMethod:)
userInfo:nil
repeats:YES];
NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
[runLoop addTimer:_myTimer forMode:NSDefaultRunLoopMode];
}

How to run NSTimer until flag variable ON in Cocoa

I have a problem: I want to NSTimer waiting until FLAG variable is YES, if FLAG = YES, myTimer is stop. How can i do that? I tried below code:
NSTimer *myTimer;
int delay = 6.0;
scanTimer= [NSTimer scheduledTimerWithTimeInterval:6.0 target:self selector:#selector(anotherfunc) userInfo:nil repeats:YES];
myTimer= [NSTimer timerWithTimeInterval: delay
target:self
selector: #selector(resetAll:) userInfo:nil
repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSModalPanelRunLoopMode];
[[NSApplication sharedApplication] runModalForWindow: scanningPanel];
This is resetAll () function :
-(void) resetAll: (NSTimer *) theTimer
{
if(FLAG)
{
NSLog(#"killWindow");
[[NSApplication sharedApplication] abortModal];
[scanningPanel orderOut: nil];
FLAG = NO;
}
else
{
delay +=6.0;
myTimer= [NSTimer timerWithTimeInterval: delay
target:self
selector: #selector(resetAll:) userInfo:nil
repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSModalPanelRunLoopMode];
[[NSApplication sharedApplication] runModalForWindow: scanningPanel];
}
}
I used 2 NSTimer, but only myTimer run, scanTimer not run. Please give me any suggestions. Thanks in advance
Why not use Key Value Observing for the FLAG change? Add the flag variable as a property of the class you're working in:
#property(nonatomic, assign) BOOL flag;
To avoid repetition place in your .m:
#define kFooFlagKey #"flag"
Override -setFlag: so it is KVO compliant:
-(void)setFlag:(BOOL)flag
{
if (_flag == flag) {
return;
}
[self willChangeValueForKey:kFooFlagKey];
_flag = flag;
[self didChangeValueForKey:kFooFlagKey];
}
In the class' initializer add self as the observer for the keypath you'd like to monitor. In this case it will be for the property "flag".
[self addObserver:self
forKeyPath:kFooFlagKey
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:NULL];
Don't forget to remove the observer in the class' -dealloc:
[self removeObserver:self forKeyPath:kFooFlagKey];
Create the timer to fire repeatedly (firingCallBack is a method called with each fire):
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.0f
target:self
selector:#selector(firingCallBack)
userInfo:nil
repeats:YES];
Implement KVO observing method:
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([keyPath isEqualToString:kFooFlagKey]) {
if (self.flag) {
[self performSelector:#selector(resetAll) withObject:nil afterDelay:0.0f];
}
}
}
Implement -resetAll however you'd like. It will be called when you are setting the flag variable via the accessor AND the flag is set to YES
Try like this:-
NSTimer *myTimer;
int delay = 6.0;
scanTimer= [NSTimer scheduledTimerWithTimeInterval:6.0 target:self selector:#selector(anotherfunc) userInfo:nil repeats:YES];
myTimer= [NSTimer scheduledTimerWithTimeInterval: delay
target:self
selector: #selector(resetAll:) userInfo:nil
repeats:NO];
[NSApp beginSheet:scanningPanel modalForWindow:[self window]
modalDelegate:self
didEndSelector:nil
contextInfo:self];
- (void)resetAll:(NSTimer *)theTimer
{
if (flag== YES)
{
[NSApp endSheet:scanningPanel];
[scanningPanel orderOut:self];
flag=NO;
}
else
{
delay +=6.0;
myTimer= [NSTimer scheduledTimerWithTimeInterval: delay
target:self
selector: #selector(resetAll:) userInfo:nil
repeats:NO];
[NSApp beginSheet:scanningPanel modalForWindow:[self window]
modalDelegate:self
didEndSelector:nil
contextInfo:self];
}
}
You just start the timer (already scheduled) and make it repeat at a relatively high frequency and stop it when the condition is met. One timer can act as two, like this:
- (void)startTimers {
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(timerFired:) userInfo:nil repeats:YES];
}
- (void)timerFired:(NSTimer *)timer {
if (self.stopTimerA && self.stopTimerB) {
[timer invalidate];
} else {
if (!self.stopTimerA)
[self timerAFired];
if (!self.stopTimerB)
[self timerBFired];
}
}
- (void)timerAFired {
// this can be coded like it has it's own timer
// we didn't pass the timer, so we can't invalidate it
// to stop...
self.stopTimerA = YES;
}
- (void)timerBFired {
// same idea here
}

Error: error: expected ')' before 'postData'

How to fix this error ?
Error: error: expected ')' before 'postData'
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:self
selector: #selector(postData:#"xyz")
userInfo:nil
repeats: YES];
Functions called as selectors from timers cannot have parameters. If I remember correctly, you can use userInfo, which passes an array or dictionary to the selector.
do something like this:
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector: #selector(postData:)
userInfo:#"xyz"
repeats: YES];
- (void)postData:(NSTimer*)timer {
NSLog(#"userInfo = %#", timer.userInfo);
}
When we read the documentation of the method you are using, it seems it's not correctly called :
timer = [NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:#selector(postData:)
userInfo:nil
repeats:YES];
And your postData must have the following signature :
- (void)postData:(NSTimer*)theTimer

How to pass an argument to a method called in a NSTimer

I have a timer calling a method but this method takes one paramether:
theTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:#selector(timer) userInfo:nil repeats:YES];
should be
theTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:#selector(timer:game) userInfo:nil repeats:YES];
now this syntax doesn't seems to be right. I tried with NSInvocation but I got some problems:
timerInvocation = [NSInvocation invocationWithMethodSignature:
[self methodSignatureForSelector:#selector(timer:game)]];
theTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval
invocation:timerInvocation
repeats:YES];
How should I use Invocation?
Given this definition:
- (void)timerFired:(NSTimer *)timer
{
...
}
You then need to use #selector(timerFired:) (that's the method name without any spaces or argument names, but including the colons). The object you want to pass (game ?) is passed via the userInfo: part:
theTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval
target:self
selector:#selector(timerFired:)
userInfo:game
repeats:YES];
In your timer method, you can then access this object via the timer object's userInfo method:
- (void)timerFired:(NSTimer *)timer
{
Game *game = [timer userInfo];
...
}
As #DarkDust points out, NSTimer expects its target method to have a particular signature. If for some reason you can't conform to that, you can instead use an NSInvocation as you suggest, but in that case you need to fully initialise it with the selector, target and arguments. Eg:
timerInvocation = [NSInvocation invocationWithMethodSignature:
[self methodSignatureForSelector:#selector(methodWithArg1:and2:)]];
// configure invocation
[timerInvocation setSelector:#selector(methodWithArg1:and2:)];
[timerInvocation setTarget:self];
[timerInvocation setArgument:&arg1 atIndex:2]; // argument indexing is offset by 2 hidden args
[timerInvocation setArgument:&arg2 atIndex:3];
theTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval
invocation:timerInvocation
repeats:YES];
Calling invocationWithMethodSignature on its own doesn't do all that, it just creates an object that is able to be filled in in the right manner.
You can pass NSDictionary with named objects (like myParamName => myObject) through userInfo parameter like this
theTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval
target:self
selector:#selector(timer:)
userInfo:#{#"myParamName" : myObject}
repeats:YES];
Then in timer: method:
- (void)timer:(NSTimer *)timer {
id myObject = timer.userInfo[#"myParamName"];
...
}