Unpause this NSTimer? - objective-c

I am pausing an NSTimer like so:
- (IBAction)pause
{
pauseStart = [[NSDate dateWithTimeIntervalSinceNow:0] retain];
previousFiringDate = [stopmusictimer fireDate];
NSDate *date = [NSDate distantFuture];
[stopmusictimer setFireDate:date];
}
I have another IBAction:
- (IBAction)unpausebutton
{
// dunno code
}
But I am not sure how to unpause the NSTimer now. Anyone, please help me! Thanks

NSTimer instances weren't really designed to be dynamically mucked with; the setFireDate: explicitly documents that doing so is relatively expensive. Probably similar to just invalidating the old one and creating a new one.
If you are pausing the timer and then your "unpause" is always "fire right now", I'd suggest invalidating and releasing the timer on pause (release only if necessary) then simply calling the targeted method on unpause.
That would be a more typical pattern.

Related

Getting Time Zone Time not updating in real time [duplicate]

I'm working with an app that processes device motion events and updates interface in 5 second increments. I would like to add an indicator to the app that would display the total time the app has been running. It seems that a stopwatch-like counter, like the native iOS Clock app is a reasonable way to count time that the app has been running and display it to the user.
What I'm not sure of is the technical implementation of such a stopwatch. Here's what I'm thinking:
if I know how long between interface updates, I can add up seconds between events and keep a count of seconds as a local variable. Alternatively, a 0.5 second interval scheduled timer can provide the count.
If I know the start date of the app, I can convert the local variable to date for each interface update using [[NSDate dateWithTimeInterval:(NSTimeInterval) sinceDate:(NSDate *)]
I can use a NSDateFormatter with a short time style to convert the updated date to a string using stringFromDate method
The resulting string can be assigned to a label in the interface.
The result is that the stopwatch is updated for each "tick" of the app.
It appears to me that this implementation is a bit too heavy and is not quite as fluid as the stopwatch app. Is there a better, more interactive way to count up time that the app has been running? Maybe there's something already provided by iOS for this purpose?
If you look in the iAd sample code from Apple in the basic banner project they have a simple timer:
NSTimer *_timer;
_timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
and the the method they have
- (void)timerTick:(NSTimer *)timer
{
// Timers are not guaranteed to tick at the nominal rate specified, so this isn't technically accurate.
// However, this is just an example to demonstrate how to stop some ongoing activity, so we can live with that inaccuracy.
_ticks += 0.1;
double seconds = fmod(_ticks, 60.0);
double minutes = fmod(trunc(_ticks / 60.0), 60.0);
double hours = trunc(_ticks / 3600.0);
self.timerLabel.text = [NSString stringWithFormat:#"%02.0f:%02.0f:%04.1f", hours, minutes, seconds];
}
It just runs from start up, pretty basic.
Almost what #terry lewis suggested but with an algorithm tweak:
1) schedule a timer
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
2) when the timer fires, get the current time (that's the tweak, don't count ticks because if there is wobble in the timer, tick counting will accumulate the error), then update the UI. Also, NSDateFormatter is a simpler and more versatile way to format time for display.
- (void)timerTick:(NSTimer *)timer {
NSDate *now = [NSDate date];
static NSDateFormatter *dateFormatter;
if (!dateFormatter) {
dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"h:mm:ss a"; // very simple format "8:47:22 AM"
}
self.myTimerLabel.text = [dateFormatter stringFromDate:now];
}

The best way to pause execution of WHILE loop in Objective-C

while(...condition...)
{
//do something
NSDate *date = [NSDate date];
NSTimeInterval milliseconds = [date timeIntervalSince1970];
[NSThread sleepForTimeInterval:0.2];
date = [NSDate date];
NSTimeInterval milliseconds1 = [date timeIntervalSince1970];
NSLog(#"**** time taken : %f",milliseconds1-milliseconds);
//calling some method
}
After 2 minutes of execution of this loop, the "time taken" increases from 200ms to 10s. Why? What is the problem ?
Assuming you are running in a multithreaded environment, answering your question would require a lot more information than you are giving us. Suffice to say, there is no guarantee that your sleeping thread will run "calling some method" exactly 200ms later, because that depends on what your other threads are doing.
Sleeping a thread like your example is generally considered a bad idea. Here's another way to accomplish what I think you are trying to do, but with ... better citizenship.
- (void)loopIfNeeded
{
if (...condition...) {
// do something
// ... and then call -someMethod ~200ms later
[self performSelector:#selector(someMethod) withObject:nil afterDelay:0.2];
}
}
- (void)someMethod
{
// whatever some method does
[self loopIfNeeded]; // continue loop
}
Finally, some observations:
-[NSDate timeIntervalSince1970] returns seconds (and fractions thereof, docs here), not milliseconds (although it has ms precision).
For timing, I find CFAbsoluteTime to be lighter weight:
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
// do your thing
CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
CFTimeInterval elapsed = start - end; // or just NSLog(#"elapsed %f", start - end);
The general comments being made suggesting this is not the way to pause your loop are correct, you should look at other ways to do this - using blocks, GCD and routines such as dispatch_after is one approach.
However, to answer your actual question: sleeping is imprecise, you sleep for at least the time specified but maybe longer. E.g. from the sleep(3) manual page:
System activity may lengthen the sleep by an indeterminate amount.
Having said that, extend 0.2s to 10s does seem quite a long stretch. Look for other activity that might be kicking in and slowing you down, if you are on Mavericks consider its aggressive actions (app nap, timer coalescing, etc.).
But really, unless this is just test code look at partitioning it into A: test condition do part 1 and B: do part 2 then call A, or some other way, and use a timer callback or GCD to handle the pause.
HTH
Your code is wrong!!!
while(...condition...)
{
//do something
NSDate *date = [NSDate date];
NSTimeInterval milliseconds = [date timeIntervalSince1970];
[NSThread sleepForTimeInterval:0.2];
date = [NSDate date];
NSTimeInterval milliseconds = [date timeIntervalSince1970]; // here should be milliseconds1
NSLog(#"**** time taken : %f",milliseconds1-milliseconds);
//calling some method
}
You're messing up your variables milliseconds and milliseconds1.

Grand Central Dispatch Concurrency

Here's my scenario....
I have a Core MIDI app that detects Note On and Note Off messages which is working nicely.
I have have some midiSend methods that send messages back to the controller to illuminate LEDs - also working fine.
What I want to do now is on the Note Off message have the LED blink on and off. This is my code:
[midiListener performSelectorOnMainThread:#selector(startTimer:) withObject:midiMsgParts waitUntilDone:YES];
-(void)startTimer:(NSDictionary *)dict {
ledIntervalCount = 0;
ledIntervalTimer = [NSTimer scheduledTimerWithTimeInterval:0.3
target:self
selector:#selector(ledIntervalLoop:)
userInfo:dict
repeats:YES];
}
-(void)ledIntervalLoop:(NSTimer *)inboundTimer{
NSDictionary *userInfo = [NSDictionary dictionaryWithDictionary:[inboundTimer userInfo]];
NSLog(#"%#", userInfo);
UInt32 onCommand = [[userInfo objectForKey:#"noteOn"] intValue];
//UInt32 offCommand = [[userInfo objectForKey:#"noteOff"] intValue];
UInt32 theNote = [[userInfo objectForKey:#"note"] intValue];
ledIntervalCount++;
if (ledIntervalCount > 3) {
[ledIntervalTimer invalidate];
ledIntervalTimer = nil;
} else {
if(ledIntervalCount %2){
[self sendNoteOnIlluminate:onCommand midiNote:theNote];
}else{
[self sendNoteOnCommand:onCommand midiNote:theNote];
}
}
}
So I'm using an NSTimer to alternate the LED on/off commands. It works fine when I press a single button but not when I press multiple ones at the same time. It seems like it only picks on the last call to the startTimer method.
This is where I think I need to implement a dispatch queue with GCD. So that each NSTimer will execute in full without being interrupted by the method calls that follow.
Am I correct? Will GCD allow me to have the NSTimer run concurrently?
GCD is a new concept to me so some guidance on how I might implement it would help. I've read through some of the reference guides but need to see some example code in the context of my scenario. I guess what I'm asking here is, what part of my code would go in the block?
AH you invalidate the timers anyway... after 3 tries. ALL -- you need X counters for X timers, you have 1 counter for X timer
instead of one long ledIntervalCount, have a NSMutableArray with ledIntervalCounts! One per timer
then in the userInfo for the timer, provide the index to the counter that is to be used
The problem was that I was calling the class from within a method wrapped in an autorelease. I now run this on the main thread and it works fine.

Why is my UIProgressBar not being updated?

Here is how I am attempting to update my UIProgressView in my iOS app.
[self.progressView setProgress:3.0/10.0 animated:YES];
// check the value.
NSLog(#"Progress: %0.1f", self.progressView.progress);
Why is NSLog outputting 0.0? Shouldn't it output 0.3? The progress bar isn't changing either.
First of all, make sure you set the progress value on the main thread. (and that self.progressView does in fact refer to your UIProgressView)
If that doesn't help, you may want to explicitly give the runloop some time to catch up on UI changes, e.g. using:
NSDate* futureDate = [NSDate dateWithTimeInterval:0.001 sinceDate:[NSDate date]];
[[NSRunLoop currentRunLoop] runUntilDate:futureDate];
However, you should be very careful using hacks like this, see:
Is calling -[NSRunLoop runUntilDate:] a good idea?
Using [[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]] to let the scheduled selectors fire
NSRunloops and forcing event processing

how do dispatch queues work

Im a bit confused here, Im using queues and I got to a point where Im a bit lost.
I have a method named getPeople who has to fetch pictures of users from the server. In order not to block the app I used this:
-(IBAction)seeMorePeople{
dispatch_queue_t getPeopleQueue = dispatch_queue_create("Pinta Ocupantes", NULL);
dispatch_async(getPeopleQueue, ^{
[self getPeople];
});
dispatch_release(getPeopleQueue);
}
The previous code is executed everytime the user taps a button. Something like "Give me pics from this album" and then another tap "Now I want people's pictures from that other album", diferent pics and different amount of pictures. If the user taps the buttons quite fast, the first queue wont finish fetching the data when the second one is already starting. With in getPeople I store the data in an NSMutableArray, so when the 2 queues are executing at the same time both are writing on the same Array and the app crashes due to out of bounds exception.
The way getPeople goes through the data is something like this:
-(void)getPeople:(NSDictionary *)peopleDictionary{
//I receive an NSDictionary and I go through it
NSArray *keys = [peopleDictionary allKeys];
int indexOfArray = 0;
for(NSString *key in keys){
//Complex operation that are not important
[peopleInArray insertObjetAtIndex:indexOfArray];//People in array is a global variable
indexOfArray++;
}
}
What I can't figure out is how to get out of this, I thought of stopping the first queue when the second one comes in, but GCD doesnt have this option... any other way to get this done, hopefully without a major recoding, anyway right now Im out of ideas, so any clue will help.
I would suggest that you avoid synchronizing with semaphores, if possible. The design of GCD is to avoid that. A background task should prepare data but not touch outside state. When the data is prepared, it should dispatch the updating of outside state to either a serial queue or, if the state is bound to the GUI, to the main queue.
So, something like this:
-(void)getPeople:(NSDictionary *)peopleDictionary{
//I receive an NSDictionary and I go through it
NSArray *keys = [peopleDictionary allKeys];
for(NSString *key in keys){
//Complex operation that are not important
dispatch_async(dispatch_get_main_queue(), ^{
[peopleInArray addObject:<whatever>];
});
}
}
If you rather want to replace the array, instead of having two threads adding to it in interleaved fashion, you'd accumulate the whole array in the background and dispatch setting the entirety of peopleInArray to the main queue.
If you want cancellation, you can implement it yourself with a flag, or you should maybe consider using NSOperation and NSOperationQueue instead of GCD. Those have a concept of cancellation built in, although your custom operations still need to check if they've been cancelled and stop working.
You are right, there is no way to stop a queue which was dispatched. One thing you could do to make sure that only one queue is accessing getPeople at the same time is using semaphores (this might be even more interesting).
If you just want to avoid that the users clicks the button multiple times you could use a bool variable stillExecuting which is set to YES in your asynchronous dispatch and set to NO at the end of getPeople. Before creating getPeopleQueue you simply check if getPeople is still executing.
if(!stillExecuting) {
dispatch_queue_t getPeopleQueue = dispatch_queue_create("Pinta Ocupantes", NULL);
dispatch_async(getPeopleQueue, ^{
[self getPeople];
});
dispatch_release(getPeopleQueue);
}
NSString *lastLoginTime =#" Your last login time";
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSDate* date1 = [dateFormat lastLoginTime];
NSDate* date2 = [dateFormat dateFromString:[NSString currentDateTime]];
NSTimeInterval secondsSinceLastLogin = [date2 timeIntervalSinceDate:date1];
// NSLog(#"Seconds Since Last Login: %g", secondsSinceLastLogin);
int hours = (int)secondsSinceLastLogin / 3600;