如何在不调用选择器的情况下分离一个线程,或者至少进行一次解决?
所以我有一个计时器视图控制器。它有一个按钮。当视图第一次加载时,按钮应该说“开始”。如何在不调用选择器的情况下分离一个线程,或者至少进行一次解决?
当它被点击时,“开始” - >“暂停”。
另一个水龙头,“暂停” - >“简历”。
另一个水龙头,“简历” - >“暂停”。因为我希望计时器准确,所以我将它从主线程中分离出来(我想我选择了正确的方法,我希望能够澄清一下)。但它似乎将它从线程中分离出来实际上调用了方法......这使按钮以“暂停”开始而不是开始。我该如何解决?为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];
}
每次定时器被激发,我们通过查找now
和self.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)
调用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;
}
}
问题是,我们需要使用testTask.timeInterval属性来设置倒数计时器,而不是现在正在执行的操作,因为它存储在核心数据中,并且需要保存timeInterval。我会如何补充? – EvilAegis
另外,你的意思是“你永远不需要将按钮的标题改为”开始“,它可以是”暂停“或”恢复“。它应该是第一次加载视图时的开始:O。 – EvilAegis
最后,这是什么意思:“计时器计划每隔0.01秒触发一次,这有望使其精确到0.1秒。”这会造成大量的内存使用情况吗? – EvilAegis