iOS - Execute a Piece of Code for 10 Seconds - objective-c

I'd like to execute a piece of code every 500ms for a total of 10 seconds. I have the code working executing every half a second with this code
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(getLevel:) userInfo:nil repeats: YES];
but I cannot seem to figure out how to stop it executing after 10 seconds. The "repeats" argument doesnt seem to allow for a specified time.
Could someone point me in the direction of where I should go with this?
Thanks
Brian

You need to message invalidate on the NSTimer that scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: gives you. For example assign the NSTimer that method returns to you in a property or iVar of your class and after 10 seconds call invalidate on it.
Example:
.h:
#interface myClass{
NSTimer *myT;
}
[...]
.m:
[...]
{
myT = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(getLevel:) userInfo:nil repeats: YES];
[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(stop) userInfo:nil repeats:NO];
}
-(void)stop{
[myT invalidate];
}
[...]
You could avoid another NSTimer if you want by implementing some kind of finish flag and track how many times you messaged the getLevel method, since it's every .5 seconds, that'll be 20 times.
But I would rather 2 NSTimer objects because you know it's 10 seconds regardless of the other timer, which you might decide to change, up or down it's frequency...

Another way you could go would be to define a selector, say -(void)stopTimer{}
in which you invalidate the timer. Then when you create the Timer in the first place do one of these:
[self performSelector:#selector(stopTimer) withObject:nil afterDelay:10.0];

The best thing to do is make a property on your class.
#property (nonatomic, strong) NSTimer *timer;
#property (nonatomic, strong) int ticks;
then in your getLevel selector call this:
if (ticks >= 20) [self.timer invalidate];
// 20 is 10 seconds * 500 milliseconds
ticks++;

You can set another timer to stop it using invalidate. Just keep a reference for this timer in an ivar or property.
Check this answer for more information.

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

NSRunLoop working OK on Simulator, Crashes iDevice

I'm working on an iOS App (FW: 5.0+ & ARC) which needs to update second by second.
Currently, I have this within a method (which is called in a performSelector when -ViewDidLoad):
-(void)FireOnload {
counter = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(countDownTime) userInfo:nil repeats:YES];
[counter fire];
[[NSRunLoop mainRunLoop] addTimer:counter forMode: NSDefaultRunLoopMode]; // <-- Causing Problem
}
-(void)countDownTime
{
double timeNow = [[NSDate date] timeIntervalSince1970];
double timeLeft = timeEnding - timeNow;
if(timeLeft < 60) // Less then 60 seconds, do something.
{
//Do Stuff
}
}
the counter variable is called in the header as NSTimer.
I explicitly call [counter fire] so it is called as soon as the view is loaded, not after 1 second.
This works fine in the iOS Simulator and will fire every second, but when it goes to the iDevice, it crashes.
I've commented out the NSRunLoop line, and the iDevice does not crash. However, it no longer updates every second.
What am I doing wrong here?
Found the Problem:
changed:
#property (weak, nonatomic) NSTimer *counter;
to:
#property (strong, nonatomic) NSTimer *counter;
If you're not using ARC, then you'll need to replace strong with retain
You say you use performSelector to invoke FireOnLoad. Why? If you are invoking it on a background thread, you need to know that NSRunLoop is not thread safe so you shouldn't access the main runloop except on the main thread.

Is it possible to change an NSTimer's userInfo?

I have an NSTimer that has an NSNumber in its userInfo
fireTimer = [NSTimer scheduledTimerWithTimeInterval:6.0
target:self
selector:#selector(fireTileAddingToColumn:)
userInfo:myNumber
repeats:YES];
After the NSTimer is created and has run a couple of times, I would like to be able to change the value of myNumber and have it reflect in fireTileAddingToColumn: I have not had any luck getting this to work. Does anyone have any suggestions?
You can always pass an object holding the userInfo to the timer:
#interface Holder
#property id data;
-(id) initWithData: (id) data;
#end
// implement it
Holder *holder = [[Holder alloc] initWithData:myNumber];
fireTimer = [NSTimer scheduledTimerWithTimeInterval:6.0 target:self
selector:#selector(fireTileAddingToColumn:) holder
repeats:YES];
[Holder setData: myNumber2];
and the data change will be reflected in the selector
You're best off creating a new timer. If the class doesn't provide an interface for changing that attribute, then you should consider it private and read-only.
It's not even possible in this case to do the usual end run around that, using KVC:
[timer setValue:newNumber forKey:#"userInfo"];
since NSTimer is not KVC-compliant for that key.
The userInfo property of an NSTimer isn't intended to be a general-purpose data storage facility. It's there so that you can have some context go along with your timer, so that the target of the timer can distinguish one timer from another if multiple timers invoke the same action.
Lazy people's mutable userInfo:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:6.0
target:self
selector:#selector(fireTileAddingToColumn:)
userInfo:#{#"mutable": [#{#"key": <value>} mutableCopy]
repeats:YES];
And to access it:
timer.userInfo[#"mutable"][#"key"] = <newValue>;

Objective C: constantly calling a function

I'm trying to make a pretty simple game and I'm stuck. Basically I want to make a UIImageView appear every 2 seconds. My problem is I can't keep track of cumulative time. Right now I have this:
NSDate *date = [NSDate date];
NSTimeInterval secondsSinceNow = [date timeIntervalSinceNow];
NSLog(#"date = %#", date);
NSLog(#"secondsSinceNow = %f", secondsSinceNow);
It's in my button function so its called when the user taps the button. It returns a decimal number always less than 1. I've tried it in the viewDidLoad method as well as it's own method but neither work.
I think it would work if its in it's own method that is check constantly, but I don't know how to do that.
In short, I need a timer/counter that updates every second.
#interface className
{
NSTimer * timer;
}
#property (nonatomic, retain) NSTimer * timer;
#end
#implementation className
#synthesize timer;
...
/*factory method was corrected here. should work without warnings by copying and pasting */
-(void) applicationDidFinishLaunching : (UIApplication *) application {
timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target:self selector:#selector(targetMethod:) userInfo:nil repeats: YES];
}
//define the target method
/*method was corrected because it needed parentheses around NSTimer */
-(void) targetMethod: (NSTimer *) theTimer {
NSLog(#"Me is here at 1 minute delay");
}
..
#end
taken from here
http://www.iphonedevsdk.com/forum/iphone-sdk-development/14403-nstimer-examples.html
If those first two lines are called immediately after each other, then it (should) always be less than a second. The date is being instantiated right there, and then the timeIntervalSinceNow is called immediately on it, when little/no time has occured between them. The goal is to make the date when first called, and then call the timeIntervalSinceNow on that to get times more than 0. However, this still has no creation of a updating timer as you want.
You could simply use an NSTimer to call a selector within your class at the required two second interval.
That said, you could possibly also make use of a CABasicAnimation to fade the opacity of the UIImageView, pending on the effect you require.

How do I use NSTimer?

Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
How do I use an NSTimer? Can anyone give me step by step instructions?
Firstly I'd like to draw your attention to the Cocoa/CF documentation (which is always a great first port of call). The Apple docs have a section at the top of each reference article called "Companion Guides", which lists guides for the topic being documented (if any exist). For example, with NSTimer, the documentation lists two companion guides:
Timer Programming Topics for Cocoa
Threading Programming Guide
For your situation, the Timer Programming Topics article is likely to be the most useful, whilst threading topics are related but not the most directly related to the class being documented. If you take a look at the Timer Programming Topics article, it's divided into two parts:
Timers
Using Timers
For articles that take this format, there is often an overview of the class and what it's used for, and then some sample code on how to use it, in this case in the "Using Timers" section. There are sections on "Creating and Scheduling a Timer", "Stopping a Timer" and "Memory Management". From the article, creating a scheduled, non-repeating timer can be done something like this:
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(targetMethod:)
userInfo:nil
repeats:NO];
This will create a timer that is fired after 2.0 seconds and calls targetMethod: on self with one argument, which is a pointer to the NSTimer instance.
If you then want to look in more detail at the method you can refer back to the docs for more information, but there is explanation around the code too.
If you want to stop a timer that is one which repeats, (or stop a non-repeating timer before it fires) then you need to keep a pointer to the NSTimer instance that was created; often this will need to be an instance variable so that you can refer to it in another method. You can then call invalidate on the NSTimer instance:
[myTimer invalidate];
myTimer = nil;
It's also good practice to nil out the instance variable (for example if your method that invalidates the timer is called more than once and the instance variable hasn't been set to nil and the NSTimer instance has been deallocated, it will throw an exception).
Note also the point on Memory Management at the bottom of the article:
Because the run loop maintains the timer, from the perspective of memory management there's typically no need to keep a reference to a timer after you’ve scheduled it. Since the timer is passed as an argument when you specify its method as a selector, you can invalidate a repeating timer when appropriate within that method. In many situations, however, you also want the option of invalidating the timer—perhaps even before it starts. In this case, you do need to keep a reference to the timer, so that you can send it an invalidate message whenever appropriate. If you create an unscheduled timer (see “Unscheduled Timers”), then you must maintain a strong reference to the timer (in a reference-counted environment, you retain it) so that it is not deallocated before you use it.
there are a couple of ways of using a timer:
1) scheduled timer & using selector
NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 2.0
target: self
selector:#selector(onTick:)
userInfo: nil repeats:NO];
if you set repeats to NO, the timer will wait 2 seconds before running the selector and after that it will stop;
if repeat: YES, the timer will start immediatelly and will repeat calling the selector every 2 seconds;
to stop the timer you call the timer's -invalidate method: [t invalidate];
As a side note, instead of using a timer that doesn't repeat and calls the selector after a specified interval, you could use a simple statement like this:
[self performSelector:#selector(onTick:) withObject:nil afterDelay:2.0];
this will have the same effect as the sample code above; but if you want to call the selector every nth time, you use the timer with repeats:YES;
2) self-scheduled timer
NSDate *d = [NSDate dateWithTimeIntervalSinceNow: 60.0];
NSTimer *t = [[NSTimer alloc] initWithFireDate: d
interval: 1
target: self
selector:#selector(onTick:)
userInfo:nil repeats:YES];
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer:t forMode: NSDefaultRunLoopMode];
[t release];
this will create a timer that will start itself on a custom date specified by you (in this case, after a minute), and repeats itself every one second
3) unscheduled timer & using invocation
NSMethodSignature *sgn = [self methodSignatureForSelector:#selector(onTick:)];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature: sgn];
[inv setTarget: self];
[inv setSelector:#selector(onTick:)];
NSTimer *t = [NSTimer timerWithTimeInterval: 1.0
invocation:inv
repeats:YES];
and after that, you start the timer manually whenever you need like this:
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer: t forMode: NSDefaultRunLoopMode];
And as a note, onTick: method looks like this:
-(void)onTick:(NSTimer *)timer {
//do smth
}
Something like this:
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
target: self
selector: #selector(handleTimer:)
userInfo: nil
repeats: YES];
#import "MyViewController.h"
#interface MyViewController ()
#property (strong, nonatomic) NSTimer *timer;
#end
#implementation MyViewController
double timerInterval = 1.0f;
- (NSTimer *) timer {
if (!_timer) {
_timer = [NSTimer timerWithTimeInterval:timerInterval target:self selector:#selector(onTick:) userInfo:nil repeats:YES];
}
return _timer;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
-(void)onTick:(NSTimer*)timer
{
NSLog(#"Tick...");
}
#end
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:#selector(timerCalled) userInfo:nil repeats:NO];
-(void)timerCalled
{
NSLog(#"Timer Called");
// Your Code
}
The answers are missing a specific time of day timer here is on the next hour:
NSCalendarUnit allUnits = NSCalendarUnitYear | NSCalendarUnitMonth |
NSCalendarUnitDay | NSCalendarUnitHour |
NSCalendarUnitMinute | NSCalendarUnitSecond;
NSCalendar *calendar = [[ NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdayComponents = [calendar components: allUnits
fromDate: [ NSDate date ] ];
[ weekdayComponents setHour: weekdayComponents.hour + 1 ];
[ weekdayComponents setMinute: 0 ];
[ weekdayComponents setSecond: 0 ];
NSDate *nextTime = [ calendar dateFromComponents: weekdayComponents ];
refreshTimer = [[ NSTimer alloc ] initWithFireDate: nextTime
interval: 0.0
target: self
selector: #selector( doRefresh )
userInfo: nil repeats: NO ];
[[NSRunLoop currentRunLoop] addTimer: refreshTimer forMode: NSDefaultRunLoopMode];
Of course, substitute "doRefresh" with your class's desired method
try to create the calendar object once and make the allUnits a static for efficiency.
adding one to hour component works just fine, no need for a midnight test (link)