Objective-C invalidate timer in another method - objective-c

I have a method in which i have declared a timer;
- (void)startTimer:(id)sender {
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval: 0.8
target: self
selector: #selector(toggleButtonImage:)
userInfo: nil
repeats: YES];
}
What i want to do is, in another method i want to invalidate the timer if it is running, here's the what i have so far but i get the error 'timer is undeclared'
- (void)stopTimer:(id)sender {
if ( [timer isValid]) {
[timer invalidate], timer=nil;
}
}
Could anyone help me?

If both methods are on the same controller, then simply make the timer an instance variable. If they are not on the same object, you should rethink your design as two classes are trying to manage the same facility.

Related

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.
}

using #selector in a class IOS

Im making a class that creates a timer that then calls a void using #selector or (SEL). The app crashes when [mytimer timer…]; is called because "unknown selector called". The error is that it isnt finding the void I'm giving to it but i know that the void works fine. My question is how to I write the myselector part correctly?
+ (void) timer:(NSTimer*)timer interval:(int)interval selector:(SEL)myselector
{
timer = [NSTimer scheduledTimerWithTimeInterval:interval
target:self
selector:myselector
userInfo:nil
repeats:YES];
}
This is the method being implemented:
[mytimer timer:NameOfTimerTheWorks interval:0.1 selector:#selector(myVoid)];
...
- (void)myVoid
{
Do stuff
}
Also I understand that this class looks completely useless but there is more to it that I didnt include. My problem revolves around the selector.
There's nothing wrong with your use of #selector(), your issue is with Objective C classes, and what self means in different scopes.
The problem is that you're creating your timer in a class + method and self at that point is not the instance you want, it's actually the Class itself. I wouldn't be doing it in a class method, but if you must, just pass in the reference to the target and it should send th message your'e expecting.
Change your implementation to the following so you pass in the target, and return the timer
+ (NSTimer *)timerWithTarget:(id)target
interval:(int)interval
selector:(SEL)myselector
{
return [NSTimer scheduledTimerWithTimeInterval:interval
target:target
selector:myselector
userInfo:nil
repeats:YES];
}
NameOfTimerTheWorks = [mytimer timetWithTarget:self interval:0.1 selector:#selector(myVoid)];
- (void)myVoid
{
//Do stuff
}
Additionally, in Objective C the standard way to name classes is with a capital letter, and camel case where appropriate. Your class should be MyTimer and not mytimer and iVars are lowercase eg nameOfTimerTheWorks.
I believe the problem is that you're trying to access an instance level method using a class level method.
I changed + to - so that it was an instance level method.
If you wanted your method to be class level it would require that you make the method you want to call also class level.
- /* <-- + */(void) timer:(NSTimer*)timer interval:(int)interval selector:(SEL)selector
{
timer = [NSTimer scheduledTimerWithTimeInterval:interval
target:self
selector:selector
userInfo:nil
repeats:YES];
}

How to use NSTimers as associative references?

I've tried the following:
- (void)setupTimer {
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(doSomething) userInfo:nil repeats:YES];
objc_setAssociatedObject(self, &key, timer, OBJC_ASSOCIATION_RETAIN);
[timer invalidate];
}
- (void)doSomething { /* ... */ }
- (void)afterDoingSomething { [objc_getAssociatedObject(self, &key) invalidate]; }
Yet, the timer doesn't tick (I didn't expected it to do so; this code just didn't look much right to me) for some reason. Is there a way to make it... work?
You're invalidating the timer immediately after creating and scheduling it. Remove the [timer invalidate]; line in -setupTimer:
- (void)setupTimer {
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(doSomething) userInfo:nil repeats:YES];
objc_setAssociatedObject(self, &key, timer, OBJC_ASSOCIATION_RETAIN);
}
From the documentation:
[invalidate] stops the receiver from ever firing again and requests
its removal from its run loop.
Am I correct in assuming that this code is in a category? Otherwise, I can't think of a good reason to make the timer an associated object instead of just using a regular instance variable for it.
You've must have mistaken invalidate and release.
objc_setAssociatedObject does release for you when you set next item.
I don't think it is a problem with setting a timer as an associative reference.
You are killing your timer before it actually start (invalidate does so).
Your - (void)doSomething method should have different signature: - (void)doSomething: (NSTimer*)theTimer.
You may be leaking memory, you should do objc_setAssociatedObject(self, &key, nil, OBJC_ASSOCIATION_RETAIN); in your afterDoingSomething.

Objective C: Call variable from another method

I'm trying to access an NSTimer in a method called getTimer in another method.
- (NSTimer *)getTimer{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector: #selector(produceBricks) userInfo:nil repeats:YES];
return timer;
[timer release];
}
I'm trying to stop the timer in another method (a method that would pause the game) by using:
if ([getTimer.timer isValid]) {
[getTimer.timer invalidate];
}
I'm assuming this is not the correct syntax being it tells me getTimer is undeclared. How would I access the timer so I can stop it?
getTimer is a method, not an object, so you can't send messages to it or access properties. Rather, assuming that the method is in the same class as the one calling it, you would call it like this:
NSTimer *timer = [self getTimer];
if ([timer isValid]) [timer invalidate];
//...
Also, you're trying to release your timer in the getTimer method after the return statement. This code will never be executed (the method has already ended) - which is good in this case, because you shouldn't release the timer, it's already autoreleased. I'd recommend that you read something on Objective-C memory management and naming conventions.
Make the timer an instance variable instead of creating in within getTimer. Then it will be accessible anywhere within the class as follows:
in MyClass.h
NSTimer* timer;
I would implement a startTimer and stopTimer method.
- (void) startTimer {
timer = [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector: #selector(produceBricks) userInfo:nil repeats:YES];
}
- (void) stopTimer {
if([timer isValid]) {
[timer invalidate];
}
}

Problems invalidating & re-creating NSTimer(s)

I'm having problems starting & stopping NSTimers. The docs say that a timer is stopped by [timer invalidate];
I have a timer object declared as such
.h
NSTimer *incrementTimer;
#property (nonatomic, retain) NSTimer *incrementTimer;
.m
#synthesize incrementTimer;
-(void)dealloc {
[incrementTimer release];
[super dealloc];
}
-The usual.
When it's needed, my method does the following:
-(void)setGenCount {
if(!condition1 && condition2) {
incrementTimer = [NSTimer scheduledTimerWithTimeInterval: 2.0
target: self
selector:#selector(incrementBatteryVoltage:)
userInfo: nil
repeats: YES];
}
}
Everything above works fine. However, once that timer does it's job, I want it to invalidate itself. I invalidate the timer because there is an equal decrement method that could be called and would fight against the incrementTimer if it was still active. (Previously, I noticed that my two timers, if active, were acting on the same ivar by increasing & decreasing the value (a sort of fight)... without crashing) The selector called works as follows:
-(void)incrementBatteryVoltage:(NSTimer *)timer {
if(battVoltage < 24.0) {
generatorDisplay.battVoltage += 0.1;
}
if(battery1Voltage == 24.0) {
[timer invalidate];
}
}
I have an equal method that Decrements the battery count. (previously mentioned)
Due to my program design: the interface simulates a voltage display. When the "machine" is turned off, I want all the timers invalidated, regardless of what any voltage value is. I'm doing this by checking to see if the timer is valid.
-(void)deEnergizeDisplays {
if([decrementTimer isValid]) {
[decrementTimer invalidate];
decrementTimer = nil;
}
if([incrementTimer isValid]) {
[incrementTimer invalidate];
incrementTimer = nil;
}
I'm getting numerous "BAD_ACCESS" crashes. The erroneous line call is always pointing toward my [timer isValid] call. It seems that if the timer is invalidated... the pointer
doesn't exist either. I know that the [timer invalidate] message disables the timer and then it is removed from the run loop and then it is released. And my understanding is: it is an autoreleased object per it's naming covention.
My thought are: If I'm sending a retain message, shouldn't the reference still exist? I've tried several combinations, taking away:
timer = nil;
or even instead of:
if([timer isValid])
I tried :
if([timer != nil])
and:
if(timer)
I always get the same crash. Thanks for any help on starting & stopping NSTimers.
UPDATE: See Darren's answer. The problem is that you are not using your property accessor when setting the timers. Instead of:
incrementTimer = [NSTimer ...
You should have:
self.incrementTimer = [NSTimer ...
The self.propertyName = ... syntax will call your accessor method, and thereby automatically retain the object that you send to it (since your property is set up as retain). Simply calling propertyName = ... does not use the property accessor. You are simply changing the value of your ivar directly.
UPDATE #2: After an enlightening conversation with Peter Hosey (see comments), I have removed my earlier suggestion to "never retain or release" your timer object. I have also completely re-written my earlier code because I think the following is a better approach:
Controller.h:
NSTimer *voltageTimer;
float targetBatteryVoltage;
...
#property (nonatomic, retain) NSTimer *voltageTimer;
Controller.m:
#implementation Controller
#synthesize voltageTimer;
- (void)stopVoltageTimer {
[voltageTimer invalidate];
self.voltageTimer = nil;
}
- (void)setTargetBatteryVoltage:(float)target {
[voltageTimer invalidate];
targetBatteryVoltage = target;
self.voltageTimer = [NSTimer scheduledTimerWithTimeInterval: 2.0
target: self
selector: #selector(updateBatteryVoltage:)
userInfo: nil
repeats: YES];
}
- (void)updateBatteryVoltage:(NSTimer *)timer {
const float increment = 0.1;
if (abs(battVoltage - targetBatteryVoltage) < increment) {
[timer invalidate];
}
else if (battVoltage < targetBatteryVoltage) {
generatorDisplay.battVoltage += increment;
}
else if (battVoltage > targetBatteryVoltage) {
generatorDisplay.battVoltage -= increment;
}
}
Now, you can simply set a target battery voltage, and the timer magic will happen behind the scenes:
[self setTargetBatteryVoltage:24.0];
Your power-off method would look as follows:
- (void)deEnergizeDisplays {
[self stopVoltageTimer];
}
You need to retain the value assigned to incrementTimer in setGenCount. You can do this automatically by using your synthesized property, which is accessed via self.:
self.incrementTimer = [NSTimer scheduledTimerWithTimeInterval: ...