Selçuk Yıldız
Selçuk Yıldız

Reputation: 579

Objective-c NSTimer, updating UILabel with timeInterval 0.01 causes app froze

#import "ActivityViewController.h"

@interface ActivityViewController ()
@property (weak, nonatomic) IBOutlet UIView *clockView;
@property (weak , nonatomic) NSTimer *timer;


@property (weak, nonatomic) IBOutlet UILabel *hoursLabel;
@property (weak, nonatomic) IBOutlet UILabel *minutesLabel;

@property (weak, nonatomic) IBOutlet UILabel *secondsLabel;

@property (weak, nonatomic) IBOutlet UILabel *miliLabel;




@end




@implementation ActivityViewController



- (void)viewDidLoad {
    [super viewDidLoad];
    
    
}

NSDate *start;

- (IBAction)pause:(UIButton *)sender {
    [self.timer invalidate];
}


-(void)startTimer {
    start = [[NSDate alloc] init];
    
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(count) userInfo:nil repeats:true];
    
    
    
  
    
    
}

- (IBAction)startCounting:(UIButton *)sender {
    [self startTimer];
}

-(void)count {
    

    NSDate *now = [[NSDate alloc] init];
    
   
    
 
    
    NSTimeInterval interval = [now timeIntervalSinceDate:start];
    self.hoursLabel.text = [NSString stringWithFormat:@"%@", [self hourString:interval]];
    self.minutesLabel.text = [NSString stringWithFormat:@"%@", [self minuteString:interval]];
    self.secondsLabel.text = [NSString stringWithFormat:@"%@", [self secondString:interval]];
    self.miliLabel.text = [NSString stringWithFormat:@"%@", [self miliString:interval]];
        
 
    
}

-(NSString *)hourString:(NSTimeInterval)timeInterval {
    NSInteger interval = timeInterval;
    long hours = (interval / 3600);
    
    return [NSString stringWithFormat:@"%0.2ld", hours];
}

-(NSString *)minuteString:(NSTimeInterval)timeInterval {
    NSInteger interval = timeInterval;
    long minutes = (interval / 60) % 60;
    
    return [NSString stringWithFormat:@"%0.2ld", minutes];
}

-(NSString *)secondString:(NSTimeInterval)timeInterval {
    NSInteger interval = timeInterval;
    long seconds = interval % 60;
    
    return [NSString stringWithFormat:@"%0.2ld", seconds];
}

-(NSString *)miliString:(NSTimeInterval)timeInterval {
 
    NSInteger ms = (fmod(timeInterval, 1) * 100);
    return [NSString stringWithFormat:@"%0.2ld", ms];
}











@end

This my whole implementation of the view controller

I try to make a stopwatch, but when I try to make this in objective-C updating UI in miliseconds slows down after 3 seconds and frozes simulator and computer after 6 seconds. Is there any solution? In swift it works very smoothly but when it comes to obj-c I have problem.

Upvotes: 0

Views: 147

Answers (2)

skaak
skaak

Reputation: 3018

I created a new default iOS project in the latest Xcode and copied and pasted and slightly fixed your code - see below. I just wired it up to the default view controller that Xcode creates as part of the project.

enter image description here

I am running this on the oldest mac mini known to man and working on another project while running this one and it gave no trouble at all. See the screenshot. Even after 5m it was still running along just fine. I continued work elsewhere no problem, nothing slowed down. I am sure it must be some other funny issue?

#import "ViewController.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIView  * clockView;
@property (weak, nonatomic) IBOutlet UILabel * hoursLabel;
@property (weak, nonatomic) IBOutlet UILabel * minutesLabel;
@property (weak, nonatomic) IBOutlet UILabel * secondsLabel;
@property (weak, nonatomic) IBOutlet UILabel * miliLabel;

@property (strong, nonatomic) NSTimer * timer;
@property (strong, nonatomic) NSDate  * start;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (IBAction)pause:(UIButton *)sender
{
    self.timer.invalidate;
}

-(void)startTimer
{
    self.start = [[NSDate alloc] init];
    self.timer.invalidate;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01
                              target:self
                            selector:@selector(count:)
                            userInfo:nil
                             repeats:true];
}

- (IBAction)startCounting:(UIButton *)sender
{
    [self startTimer];
}

- ( void ) count:( NSTimer * ) timer
{
    NSDate *now = [[NSDate alloc] init];

    NSTimeInterval interval = [now timeIntervalSinceDate:self.start];
    self.hoursLabel.text = [NSString stringWithFormat:@"%@", [self hourString:interval]];
    self.minutesLabel.text = [NSString stringWithFormat:@"%@", [self minuteString:interval]];
    self.secondsLabel.text = [NSString stringWithFormat:@"%@", [self secondString:interval]];
    self.miliLabel.text = [NSString stringWithFormat:@"%@", [self miliString:interval]];
}

-(NSString *)hourString:(NSTimeInterval)timeInterval {
    NSInteger interval = timeInterval;
    long hours = (interval / 3600);

    return [NSString stringWithFormat:@"%0.2ld", hours];
}

-(NSString *)minuteString:(NSTimeInterval)timeInterval {
    NSInteger interval = timeInterval;
    long minutes = (interval / 60) % 60;

    return [NSString stringWithFormat:@"%0.2ld", minutes];
}

-(NSString *)secondString:(NSTimeInterval)timeInterval {
    NSInteger interval = timeInterval;
    long seconds = interval % 60;

    return [NSString stringWithFormat:@"%0.2ld", seconds];
}

-(NSString *)miliString:(NSTimeInterval)timeInterval {

    NSInteger ms = (fmod(timeInterval, 1) * 100);
    return [NSString stringWithFormat:@"%0.2ld", ms];
}

@end

This is some other issue I am sure ... I assume your ActivityViewController just derives straight from a normal UIViewController and that your clockView is not doing anything funny.

Anyhow, see if this helps but I think the issue is elsewhere.

Upvotes: 1

skaak
skaak

Reputation: 3018

Several things ... you dispatch on the main queue but you are already on the main queue so you are swamping the main queue. Also, you dispatch async so the queue gets clogged pretty soon I think. Still I would think the hardware should be able to handle it actually so I am not entirely sure why it freezes, but I think you need to do it a bit better.

Also, importantly, if you build a stopwatch you should not count yourself. Sure, use a timer to regularly update the stopwatch, but in stead of counting yourself you should use the wall clock for the time. The way you do it now will be very inaccurate and the inaccuracies will accumulate over time.

At present you fire every 10 millis. Maybe fire every 100 and then update your stopwatch but read from the device's internal clock. This in itself is problematic, see How can I get the Correct Current Time in iOS? for some ideas on how to do that.

When the stopwatch starts note the time e.g.

NSDate * start = NSDate.now;

and when the timer fires calculate the difference from this start time using the current time, again with something like

NSDate * now = NSDate.now;

and update your stopwatch with the difference between the two. This will be both easier and more accurate BUT see that link as it is also needs some improvement and is just a starting point for now.

Upvotes: 1

Related Questions