Does NSTimer run across entire app? - objective-c

If I start an NSTimer like this:
#property (strong) NSTimer * messageTimer;
self.messageTimer = [NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:#selector(checkForMessages)
userInfo:nil
repeats:YES];
Does it continue to run when I switch to different view controllers?
Until I cancel it with:
[messageTimer invalidate]; self. messageTimer = nil;

Yes.
Okay, now here is an extended description. NSTimer registers itself on nearest NSRunLoop, that is, current dispatch loop (they may nest). This loop asks various sources for events and calls corresponding callbacks.
When it is time for NSTimer to fire, it returns YES to NSRunLoop and that runs passed callback. There is no such thing as "other current view controller". It is all about first responder and view hierarchy, neither doesn't have any effect on run loops.

Related

Any bugs or changes in NSTimer with OC?

I'm testing NSTimer with OC and Swift. When I write with OC, here is something I don't quite understand: is it REQUIRED to add the timer to the NSRunLoop after the timer is inited? If not required, why can't it be invoked in a loop even if I set the repeats to YES?
Here is my code, I just init a timer and set repeats to YES, and what I expect is the code timerTick: should be invoked every 2 seconds, but it doesn't work as I expect... until I add the timer to the NSRunLoop.
OC:
-(void)viewDidLoad {
[super viewDidLoad];
NSTimer *timer = [NSTimer timerWithTimeInterval:2 target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
}
-(void) timerTick:(NSTimer*) timer{
NSLog(#"ticked,%#",#"aa");
}
I rewrote the same code with Swift
As you can see, I don't add the timer to NSRunLoop, however it works as I expected: the runTimeCode method is invoked every 2 seconds.
var timer:NSTimer!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "runTimeCode", userInfo: nil, repeats: true)
}
func runTimeCode(){
NSLog("ticked")
}
My Question
Is there any bug in OC with iOS9.2 of NSTimer? I searched a lot with Google, but I didn't find anything saying it is REQUIRED if you want to let the timer works correctly.
How do I use NSTimer?
Is NSTimer usage different between OC and Swift?
First of all, you are using different methods. How come you would get the same result? scheduledTimerWithTimeInterval will automatically add to the NSRunLoop after initializing it. However, timerWithTimeInterval won't. You will need to manually call fire or add it to the NSRunLoop in order to trigger it.
Just like the methods' name mean, scheduledTimerWithTimeInterval will do scheduling for you. As for timerWithTimeInterval, it just gives you a timer. Apple is pretty restricted on the names. Sometimes, you can guess its purpose based on it.
With Objective-C, you should try this method :
NSTimer *timer = [NSTimer scheduleTimerWithTimeInterval:2 target:self userinfo:...];
You can see all method's content in Xcode.

Concurrent processing on main queue during UITableView scroll

I have a stopwatch screen in my app.
There is a main label indicating passing time every 100 milliseconds (updates constantly).
Just below that label, in the same ViewController I have a UITableView.
Whenever I'm scrolling the UITableView, the stopwatch labels stops updating.
I've tried using Grand Central Dispatch as follows, but it doesn't work and I didn't expect it would, since it's still two operations running on the same queue.
dispatch_async(dispatch_get_main_queue(),^{
MyTimerObject *t = (MyTimerObject*)notification.object;
lblMainTimer.text = t.MainTimerStringValue;});
So how should I approach this problem?
This all happens within the receiveNotification method of NSNotification, which make me wonder if it's not NSNotification that locks up during the table scroll event and not the label update...
Is your stopwatch updating with NSTimer or CADisplayLink? The issue associated with failure to update while scrolling is generally an incorrect choice of run loop modes.
For example, this will pause while tableview is scrolling.
NSTimer *timer = [NSTimer timerWithTimeInterval:0.1 target:self selector:#selector(handleTimer:) userInfo:nil repeats:TRUE];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
Whereas this will not:
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
Bottom line, don't use NSDefaultRunLoopMode, but rather use NSRunLoopCommonModes, and you may find it continues to run even when scrolling the table view.

How do I replace this NSTimer with a CADisplayLink?

I realize that a CADisplayLink would be better suited for the nature of my current project, however, i can't quite figure out how to implement a CADisplayLink and replace my NSTimer.
below is the code for my NSTimer
Movement = [NSTimer scheduledTimerWithTimeInterval:0.002 target:self selector:#selector(BarMoving) userInfo:nil repeats:YES];
how can I create a CADisplayLink that will perform the same function but more efficiently?
Create the thing:
_displayLink = [CADisplayLink displayLinkWithTarget:self
selector:#selector(BarMoving)];
Start it running:
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
... that'll cause your display link to issue calls to BarMoving on the main run loop (which is the one associated with the main thread and therefore the main queue) whenever that run loop is in the default mode. So things like when the user has their finger down scrolling a scroll view will pause your timer. NSTimer has the same default behaviour.

Flipping a UISwitch with an NSTimer

I'm trying to learn how to use NSTimers, and I thought of the following: Create a switch. Let the timer begin as the app begins, and after each second, a function that changes the state of the switch is called.
Here's what I did so far:
I declared both the timer and the switch in the header file ViewControl.h:
//Timer
{NSTimer *timer;}
#property (weak, nonatomic) IBOutlet UISwitch *zeSwitch;
Then, in the ViewControl.m file I defined the following:
- (IBAction)zeSwitch:(id)sender {
UISwitch *zeSwitchSatus = (UISwitch *) sender;
BOOL yn = zeSwitchSatus.isOn;
[zeSwitch setOn:yn animated:YES];
}
- (void)viewDidLoad
{
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self
selector:#selector(zeSwitch) userInfo:nil repeats:YES];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
So my hope was that when I run the code, I'll see a switch that is on or off. Then I'll see it changing its status automatically with time, without me interfering.
But that didn't work! I first get the image above. Nothing changes. Then it crashes when I press the switch. (But my idea is not to touch it at all.)
Any ideas?
You're pretty close. There's a few things wrong here. First, the method that you're giving to the timer is named zeSwitch: -- the colon is significant. So you need to create the timer like this:
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(zeSwitch:)
userInfo:nil //^ Note colon!
repeats:YES];
The method named zeSwitch without the colon is actually the accessor method for the switch, because you've named your property zeSwitch. You should really rename the timer's action method to clarify this. Right now, the timer is calling the accessor method for the switch every second, which doesn't really do anything.
Next, the timer passes itself to the method it calls. The sender argument in zeSwitch: is going to be the timer, not the switch. If this method was actually being called via the timer, you would get a crash because you'd be sending isOn to the timer, and it doesn't respond to that.
You've got an outlet to the switch, so you can refer to it via that outlet:
- (void)flipSwitch: (NSTimer *)tim
{
BOOL switchIsOn = [[self zeSwitch] isOn];
Notice that I've corrected the names and types in this method -- you'll also need to change the timer creation to reflect this: #selector(flipSwitch:).
Third, you want to flip the switch, so you should be setting it to the opposite of its current status. The next line needs to be:
[[self zeSwitch] setOn:!switchIsOn animated:YES];
The ! operator negates the BOOL to which it's attached, turning YES into NO and vice versa.
1) When you specify a selector that takes one parameter, you need a colon after the name, so #selector(zeSwitch:).
2) The selector that is triggered by a timer gets the timer as a parameter, not a switch, so - (IBAction)zeSwitch:(NSTimer *)timer.

NSTimer not working

I have set up an NSTimer which after one second should perform a instance method called animate
My code looks like this:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(animate) userInfo:nil repeats:NO];
[timer fire];
This code is put into a touchesBegan method. The method DOES GET CALLED but not after one second it just immediately gets called. Why is this?
You've scheduled a timer and normally that should automatically invoke after 1.0 seconds, but you follow up with a [timer fire] call and that is immediately firing the timer and sending a message to the selector.
Look at the documentation.
Delete [timer fire];
That will solve your problem.
When you call [timer fire] it immediately fires the message to the receiver. You just need to remove that line.
Calling fire causes the message to be sent to its target immediately. See the documentation here.