如何在不调用选择器的情况下分离一个线程,或者至少进行一次解决?

问题描述:

所以我有一个计时器视图控制器。它有一个按钮。当视图第一次加载时,按钮应该说“开始”。如何在不调用选择器的情况下分离一个线程,或者至少进行一次解决?

当它被点击时,“开始” - >“暂停”。

另一个水龙头,“暂停” - >“简历”。

另一个水龙头,“简历” - >“暂停”。因为我希望计时器准确,所以我将它从主线程中分离出来(我想我选择了正确的方法,我希望能够澄清一下)。但它似乎将它从线程中分离出来实际上调用了方法......这使按钮以“暂停”开始而不是开始。我该如何解决?为testTask.showButtonValue

顺便说一句,默认值(带负载)为1

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    [NSThread detachNewThreadSelector:@selector(startTimer:) toTarget:self withObject:nil]; 

    if (testTask.showButtonValue == 1) { 
     [startButton setTitle:@"Start" forState:UIControlStateNormal]; 
    } else if (testTask.showButtonValue == 2) { 
     [startButton setTitle:@"Pause" forState:UIControlStateNormal]; 
    } else if (testTask.showButtonValue == 3){ 
     [startButton setTitle:@"Resume" forState:UIControlStateNormal]; 
    } 
} 
-(IBAction)startTimer:(id)sender{ 
    if (testTask.showButtonValue == 1) { 
     [startButton setTitle:@"Pause" forState:UIControlStateNormal]; 
     timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES]; 
     testTask.showButtonValue = 2; 
    } else if (testTask.showButtonValue == 2) { 
     [startButton setTitle:@"Resume" forState:UIControlStateNormal]; 
     [timer invalidate]; 
     timer = nil; 
     testTask.showButtonValue = 3; 
    } else if (testTask.showButtonValue == 3){ 
     [startButton setTitle:@"Pause" forState:UIControlStateNormal]; 
     timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES]; 
     testTask.showButtonValue = 2; 
    } 
} 
-(void)timerAction:(NSTimer *)t 
{ 
    if(testTask.timeInterval == 0) 
    { 
     if (self.timer) 
     { 
      [self timerExpired]; 
      [self.timer invalidate]; 
      self.timer = nil; 
     } 
    } 
    else 
    { 
     testTask.timeInterval--; 
    } 
    NSUInteger seconds = (NSUInteger)round(testTask.timeInterval); 
    NSString *string = [NSString stringWithFormat:@"%02u:%02u:%02u", 
         seconds/3600, (seconds/60) % 60, seconds % 60]; 
    timerLabel.text = string; 
    NSLog(@"%f", testTask.timeInterval); 
} 

我建议要对此以不同的方式:

@interface SNDViewController() 
@property (weak, nonatomic) IBOutlet UIButton *startButton; 
@property (weak, nonatomic) IBOutlet UILabel *timerLabel; 
@property (nonatomic, strong) NSTimer *timer; 
@property (nonatomic, assign) NSTimeInterval accumulativeTime; 
@property (nonatomic, assign) NSTimeInterval currentReferenceTime; 
@end 

@implementation SNDViewController 

编辑:当视图加载,初始化self.accumulativeTime和更新定时器标签。 accumulativeTime变量的初始化应该在适当的init *方法中完成,例如, initWithNibName:束:.在这一点上,您将从核心数据中读取定时器值。

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    self.accumulativeTime = 300; 
    [self updateTimerLabelWithTotalTime:self.accumulativeTime]; 
} 

- (IBAction)changeTimerState:(UIButton *)sender 
{ 
    if (self.timer == nil) { 
     self.currentReferenceTime = [NSDate timeIntervalSinceReferenceDate]; 
     [self.timer invalidate]; 
     self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(updateTimer:) userInfo:nil repeats:YES]; 
    } else { 
     //Pause the timer and track accumulative time. 
     NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; 
     [self.timer invalidate]; 
     self.timer = nil; 
     NSTimeInterval timeSinceCurrentReference = now - self.currentReferenceTime; 

编辑:从accumalitiveTime减法timeSinceCurrentReference,因为这是一个倒计时的计时器。如有必要,还添加了保存到核心数据的注释。

 self.accumulativeTime -= timeSinceCurrentReference; 
     [self updateTimerLabelWithTotalTime:self.accumulativeTime]; 

     //Optionally save self.accumulativeTime to core data for future use. 
    } 

    NSString *buttonTitle = (self.timer != nil) ? @"Pause" : @"Resume"; 
    [self.startButton setTitle:buttonTitle forState:UIControlStateNormal]; 
} 

您不需要存储任何不必要的状态,例如showButtonValue变量。相反,您可以根据您是否暂停或恢复self.timer == nil作出决定。如果没有定时器运行,则抓住当前的参考时间并启动一个新的定时器。计时器计划每0.01秒触发一次,这有望使其精确到0.1秒。您永远不需要将按钮的标题更改为“开始”。它可以是“暂停”或“恢复”。 当用户暂停计时器时,我们会处理self.timer并更新计时器标签以获得最准确的时间。

- (void)updateTimer:(id)sender 
{ 
    NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; 
    NSTimeInterval timeSinceCurrentReference = now - self.currentReferenceTime; 

编辑:减去从self.accumulativeTime timeSinceCurrentReference得到TOTALTIME(即TOTALTIME减小随着时间的推移)。

NSTimeInterval totalTime = self.accumulativeTime - timeSinceCurrentReference; 

    if (totalTime <= 0) { 
     totalTime = 0; 
     [self.timer invalidate]; 
     self.timer = nil; 

     //What to do when we reach zero? For example, we could reset timer to 5 minutes: 
     self.accumulativeTime = 300; 
     totalTime = self.accumulativeTime; 
     [self.startButton setTitle:@"Start" forState:UIControlStateNormal]; 
    } 

    [self updateTimerLabelWithTotalTime:totalTime]; 
} 

每次定时器被激发,我们通过查找nowself.currentReferenceTime,并把它添加到self.accumulativeTime之间的差异获取的总时间。

- (void)updateTimerLabelWithTotalTime:(NSTimeInterval)totalTime 
{ 
    NSInteger hours = totalTime/3600; 
    NSInteger minutes = totalTime/60; 
    NSInteger seconds = totalTime; 
    NSInteger fractions = totalTime * 10; 

    self.timerLabel.text = [NSString stringWithFormat:@"%02u:%02u:%02u.%01u", hours, minutes % 60, seconds % 60, fractions % 10]; 
} 

@end 

方法- (IBAction)changeTimerState:(UIButton *)sender由UIButton的关于 “润色Inside” 的事件(UIControlEventTouchUpInside)调用。

你不需要在viewDidLoad中做任何事情。

此外,重要的是,这些都是在主线程上完成的。如果有什么妨碍更新计时器标签的主线程,则用户可见的文本可能不准确,但更新时,可以确保它再次准确。这取决于你的应用正在做什么。但是由于所有UI更新都必须在主线程上完成,所以实际上并没有避免这种情况。

希望这会有所帮助。如果有什么不清楚,请告诉我。

(的Xcode可以在这里找到项目:https://github.com/sdods3782/TVTTableViewTest

+0

问题是,我们需要使用testTask.timeInterval属性来设置倒数计时器,而不是现在正在执行的操作,因为它存储在核心数据中,并且需要保存timeInterval。我会如何补充? – EvilAegis

+0

另外,你的意思是“你永远不需要将按钮的标题改为”开始“,它可以是”暂停“或”恢复“。它应该是第一次加载视图时的开始:O。 – EvilAegis

+0

最后,这是什么意思:“计时器计划每隔0.01秒触发一次,这有望使其精确到0.1秒。”这会造成大量的内存使用情况吗? – EvilAegis

调用detachNewThreadSelector将创建一个新的线程,并执行选择所提到的,立即AFER呼叫。

用于固定您的问题改变喜欢你的方法:

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    [NSThread detachNewThreadSelector:@selector(startTimer:) toTarget:self withObject:nil]; 
} 
-(IBAction)startTimer:(id)sender 
{ 
    if (testTask.showButtonValue == 1) { 
     [startButton setTitle:@"Start" forState:UIControlStateNormal]; 
     timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES]; 
     testTask.showButtonValue = 3; 
    } else if (testTask.showButtonValue == 2) { 
     [startButton setTitle:@"Resume" forState:UIControlStateNormal]; 
     [timer invalidate]; 
     timer = nil; 
     testTask.showButtonValue = 3; 
    } else if (testTask.showButtonValue == 3){ 
     [startButton setTitle:@"Pause" forState:UIControlStateNormal]; 
     timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES]; 
     testTask.showButtonValue = 2; 
    } 
} 
+0

是不是UIKit中正在比这个代码mainthread其他线程使用? – hfossli

+0

这工作,但在我做出正确的答案之前,你确实删除了viewDidLoad中的部分代码:O.当用户再次按下并转到视图时,它不会显示按钮的正确标签:/。我不知道如何解决这个问题.. – EvilAegis

+0

嗯..这似乎是如果我开始它,然后按回去,然后再次进入视图,它实际上被分离到线程TWICE(或更多),这使得该计时器的速度降低了2倍:O!嗯... – EvilAegis