Observer pattern for stopwatch - objective-c

I'm trying to implement a stopwatch based on the MVC model.
The stopwatch uses the NSTimer with the selector -(void) tick being called every timeout.
I've tried to make the stopwatch as a model for reusability but I've run into some design problems regarding how to update the view controller for each tick.
First I created a protocol with the tick method and made the view controller its delegate. The view controller then updates the views based on the timer's properties at each tick. elapsedTime is a readonly NSTimeInterval.
It works, but I'm thinking it might be bad design. I'm an Objective-C/Cocoa Touch beginner. Should I be using something like KVO? Or is there a more elegant solution for the model to notify the view controller that elapsedTime has changed?

The timer is a good way to make sure that you update your user interface periodically, but don't use it to keep track of time. NSTimer can drift, and any small errors can accumulate if you use a timer to accumulate seconds.
Instead, use NSTimer to trigger a method that updates your UI, but get the real time using NSDate. NSDate will give you millisecond resolution; if you really need better than that, consider this suggestion to use Mach's timing functions. So, using NSDate, your code might be something like this:
- (IBAction)startStopwatch:(id)sender
{
self.startTime = [NSDate date];
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(tick:)
userInfo:repeats:YES];
}
- (void)tick:(NSTimer*)theTimer
{
self.elapsedTime = [self.startTime timeIntervalSinceNow];
[self updateDisplay];
}
- (IBAction)stopStopwatch:(id)sender
{
[self.timer invalidate];
self.timer = nil;
self.elapsedTime = [self.startTime timeIntervalSinceNow];
[self updateDisplay];
}
Your code might be a little more sophisticated if you allow restarting, etc., but the important thing here is that you're not using NSTimer to measure total elapsed time.
You'll find additional helpful information in this SO thread.

I would recommend against KVO for this problem. It introduces a lot of complexity (and several annoying gotchas) for little benefit here. KVO is important in cases where you need to ensure absolutely minimal overhead. Apple uses it a lot in cases for low-level, high-performance objects like layers. It is the only generally-available solution that offers zero-overhead when there is no observer. Most of the time, you don't need that. Handling KVO correctly can be tricky, and the bugs it can create are annoying to track down.
There's nothing wrong with your delegate approach. It's correct MVC. The only thing you need to really worry about is that NSTimer doesn't make strong promises about when it's called. A repeating timer is even allowed to skip in some cases. To avoid that problem, you generally want to calculate elapsedTime based on the current time rather than by incrementing it. If the timer can pause, then you need to keep an accumulator and a "when did I last start" date.
If you need higher-accuracy or lower-cost timers, you can look at dispatch_source_set_timer(), but for a simple human-targeted stopwatch, NSTimer is fine, and an excellent choice for a simple project.

Lately, I have been using using blocks instead of plain old #selector's. It creates better and code and keeps the logic on the same location.
There's no native blocks support in NSTimer, but I used a category from https://gist.github.com/250662/d4f99aa9bde841107622c5a239e0fc6fa37cb179
Without the return selector, you keep the code in one spot:
__block int seconds = 0;
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:1
repeats:YES
usingBlock:^(NSTimer *timer) {
seconds++;
// Update UI
if (seconds>=60*60*2) {
[timer invalidate];
}
}];

Related

Deallocating window when animations may be occurring

My AppDelegate maintains a list of active window controllers to avoid ARC deallocating them too early. So I have a notification handler like this:
- (void) windowWillClose: (NSNotification*) notification {
[self performSelectorOnMainThread: #selector(removeWindowControllerInMainThread:)
withObject: windowController
waitUntilDone: NO];
}
- (void) removeWindowControllerInMainThread: (id) windowController {
[windowControllers removeObject: windowController];
}
I use the main thread because doing the handling on the notification thread risks deallocating the controller before it's ready.
Now, this works pretty well — except when there are animators currently running. I use animators in some places, through NSAnimationContext. I have looked at this QA, and the answer just isn't acceptable. Waiting for a while, just to get animation done, is really shoddy and not guaranteed to work; indeed it doesn't. I tried using performSelector:withObject:afterDelay, even with a larger delay than the current animation duration, and it still results in the animator running against nil objects.
What is the preferred way of doing controller cleanup like this? Not use NSAnimationContext but using NSAnimation instead, which has a stopAnimation method?
First, if some of your animations run indefinitely -- or for a very long time -- you're going to have to have a way to stop them.
But for things like implicit animations on views, you could simply use a completion method.
self.animating=YES;
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
[[v animator] setAlphaValue: 1];
} completionHandler:^{
self.animating=NO;
}];
Now, you only need to poll whether your animation is running and, if it's not running, proceed to close your window.
One nice way to do the polling is to set a timer with a fixed delay. If the animation is still running, just reset the timer and wait another interval.
Alternatively, you could send a notificaton from the completion handler.
I haven't used NSAnimationContext (always did this with NSAnimation, but mostly for historical reasons). But the typical way I like to managed things similar to this is to create short-lived retain loops.
Mark's answer is exactly the right kind of idea, but the polling is not required. The fact that you reference self in the completion handler means that self cannot deallocate prior to the completion handler running. It doesn't actually matter whether you ever read animating. ARC has to keep you around until the completion block runs because the block made a reference to you.
Another similar technique is to attach yourself to the animation context using objc_setAssociatedObject. This will retain you until the completion block runs. In the completion block, remove self as an associated object, and then you'll be free to deallocate. The nice thing about that approach is that it doesn't require a bogus extra property like animating.
And of course the final, desperate measure that is occasionally appropriate is to create short-lived self-references. For instance:
- (void)setImmortal:(BOOL)imortal {
if (immortal) {
_immortalReference = self;
}
else {
_immortalReference = nil;
}
}
I'm not advocating this last option. But it's good to know that it exists, and more importantly to know why it works.

Time Intervals in Objective C

I am very very new to Objective C and x-code. I just started learning it last week. so sorry if the question is too simple.
I am doing a game more like a visual novel so its very simple and story board based app, but I have a question regarding displaying my choices. I want the choices to appear after my cut scene ends. is there any way I can add something like time-interval breaks so they only appear after certain time. can some one please guide me on how to go about it.
You can use NSTimer:
[NSTimer scheduledTimerWithTimeInterval:2.0f
target:self
selector:#selector(displayChoiceTimerFired:)
userInfo:nil
repeats:NO];
This will schedule a timer that will execute the displayChoiceTimerFired: method after 2 seconds. (The argument of that method is the timer itself.)
Or you can also use NSObject's perform methods:
[self performSelector:#selector(displayChoice:) withObject:nil afterDelay:2.0f];
Depending on how you are displaying your cut scenes, the safer way to display these UI elements would be to somehow trigger an action when your cut scene ends. The problem with the NSTimer Implementation I can foresee is if, for whatever reason, your cut scene takes longer to play than your NSTimer interval is set to, then your UI Elements are displayed prematurely. Just my two cents on a different, but what I believe to be a more elegant approach.
[self performSelector:#selector(displayChoice:) withObject:nil afterDelay:1.0];
You can use this method...where after delay is number of seconds

NSTimer: Getting firing to NOT act retroactively

I'm currently using the snippet of code presented below to fire some methods every second. My app is running in the background. The problem is that if the computer wakes up after a sleep period the timer wants to retroactively fire all the methods it has missed. Similar issues come up if the user were to change the System Clock time.
Basically I want to implement the proper timer method that will have my methods called only every current second. If a second (or minute or hour or day) has passed and for whatever reason the methods weren't called I want my app to just continue from the current moment in time.
Also, can we keep this while using NSTimer?
Thanks!
-(void)start
{
NSTimer * timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(tasks:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode: NSDefaultRunLoopMode];
}
To handle the big time changes you can use the link UIApplicationSignificantTimeChangeNotification and unregister/reregister your timer.
To deal with the sleep issue, you can unregister and then reregister your timer whenever the machine goes to sleep and wakes up. See this technical note for information on how to do that. This solution won't work for changing the system time, though.

How should the model update the UI of its progress?

I am trying to solve a problem in Objective-C, but I don't think the question is language specific.
I have to do some processing down in a model class that has no notion of UI. However, this processing takes some time and I want to let the user know the status via a progress bar.
My first attempt at this was defining a notion of a progress handler protocol/interface with some methods like
-startOperation;
-updateProgress:(double)currentValue ofMax:(double)maxValue
-endOperation;
This way my UI can implement that the the model need not know details about what goes on other than someone wants progress updates. Currently my UI unhides a progress bar, and updates it, then hides it when done. So far so good.
However, it turns out that sometimes this operation processing is very fast. Such that the UI updates result in a pretty disconcerting flicker as they execute. I don't know if the operation will be fast or slow beforehand.
One idea I had was to force the operation to take at least a certain duration to avoid the UI changes being so jarring to the eye, but this seemed to put knowledge of the UI in the model class, which must be wrong.
This would seem to be a common issue with (hopefully) some known pattern.
How would you address this?
Jonathan's and Darren's answers cover your actual problem, but I would add something regarding the question in the title: "How should the model update the UI of its progress?"
The answer, of course, is that it shouldn't. The model shouldn't have to know anything about any protocols for displaying data. There should be one uniform bindings layer taking care about propagating information from the model to the interface. Fortunately, Cocoa already includes such a bindings mechanism: Key-Value Observing.
What you should do is define a property on any model class where the concept of progress makes sense, something like #property (assign) float progress. Then you make sure the class is KVO compliant. Controller code that want to keep track of the progress simply registers to observe this value with something like:
[theObject addObserver:self forKeyPath:#"progress" options:0 context:NULL];
Make sure to read the documentation for the NSKeyValueObserving (KVO) informal protocol.
Also, you might want to have a look at Mike Ash's KVO-related notes and code: Key-Value Observing Done Right.
You can use NSTimer to delay the display of your progress bar until your operation had run for a given amount of time, say half a second:
-(void)startOperation {
// Show the progress bar in 0.5 seconds
if (!_timer) {
_timer = [[NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(showProgressBar:)
userInfo:nil
repeats:NO] retain];
}
}
In -endOperation, you cancel the timer and hide progress bar:
-(void)endOperation {
[_timer invalidate]; // cancel the timer
[_timer release];
_timer = nil;
[self hideProgressBar];
}
If the operation completes in less than 0.5 seconds, the timer is canceled before the progress bar is displayed.
One thing commonly done is to have your progress bar implementation not show itself right away, and apply some heuristic based on the first couple of updates (or a timeout) to determine whether it needs to show itself at all. That's how the Java ProgressMonitor behaves, for example. (The ProgressMonitor is a nice abstraction that separates the knowledge of progress from its graphical representation).
Once the progress widget is showing, you could repaint it on a leisurely timer, say 10 times per second, rather than reacting to every progress change event with a repaint.

"scheduledTimerWithTimeInterval:" problem in cocos2d?

I am trying to developed a iPhone app by using cocos2d. I using "scheduledTimerWithTimeInterval" for calling a method that called a fixed time interval. But now time interval increases in gradually. for this reason the time is slow in gradually.
here is my code:
- (void) methodTime: (id) sender{
NSTimer *rat =[NSTimer scheduledTimerWithTimeInterval:(.5) target:self selector:#selector(rotation:) userInfo:nil repeats:YES];
}
- (void) rotation:(NSTimer *)theTimer{
NSLog(#"I m # %i", i);
i=i+10; // Here , i is a global int variable.
i=i % 1440;
if(i==0){
[theTimer invalidate];
}
else {
int rotationNum=i;
Sprite *sp = [Sprite spriteWithFile: #"1.png"];
sp.position=cpv(220,180.5);
sp.rotation=rotationNum;
[self add:sp];
}
}
It looks like every 0.5 seconds you are adding a sprite to some list of sprites. Eventually the list is getting very large and all that data causes your method to take longer than 0.5 seconds to execute. This causes the timer to fire fast as it can, which is not all that fast since its always waiting for your method to be finished with.
Without know more about your code, that's my best guess.
Don't use NSTimer. Check out cocos2d best practices.
I had trouble understanding your question. You haven't even used one quotation mark in your 'question'.
Is it possible that your code needs more than 0.5 seconds to execute? Especially because the amount of work done in the background increases each iteration. (More sprites.)
Try to make this function faster, for example load that PNG into memory and don't load it each time from the file. (That's better for memory anyway.)
Btw: That loop looks dangerously like an infinite loop. (Though could be that it works…)
If all you're doing is continually rotating the object, CATransforms and Core Animation might be a better bet.
I would use Cocos2d-iphone's built in intervals.
Go through the demo cocos project and you will see how things like this "should" be done in the cocos framework.