我正在建立一个"监控"我的iPhone上的应用程序。我正在使用AFNetworking-2.0。我有一个后端服务器公开一个用Python3 / tornado编写的RESTful接口。
根据我ViewController
的级别,我想用不同的查询轮询不同的数据(应用程序的焦点调整查询的焦点)。为了#34;让它工作",我已经设置了以下内容:
#pragma mark - Pull Loop
- (void) forkPull {
NSString* uri = [NSString stringWithFormat: @"%@/valves", Site.current.serialID];
[[HttpConnection current]
GET: uri
parameters: @{}
success:^(NSURLSessionDataTask* task, id responseObject){
[Site.current performSelectorOnMainThread: @selector(fromDoc:) withObject:responseObject waitUntilDone:YES];
NSTimeInterval delay = 60; // default poll period
// attempt to hone in if we have valid lastTouch info
if (Site.current.touched != nil) {
NSDate *futureTick = [Site.current.touched dateByAddingTimeInterval: 65];
if ([futureTick compare: [NSDate date]] == NSOrderedDescending) {
delay = futureTick.timeIntervalSinceNow;
}
}
[self performSelector: @selector(forkPull) withObject:nil afterDelay:delay];
NSLog(@"%@ forkPull again in %f", self, delay);
}
failure:^(NSURLSessionDataTask* task, NSError* error){
NSLog(@"%@ forkPull error: %@ (uri=%@)", self, error, uri);
[self performSelector: @selector(forkPull) withObject:nil afterDelay:60];
}
];
}
- (void) stopPull {
[NSObject cancelPreviousPerformRequestsWithTarget: self];
}
#pragma mark - View Management
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear: animated];
....
[self forkPull]; // start up polling while I'm visible
}
-(void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self stopPull]; // I'm going away, so shut down the pull loop?
...
}
基本上,当控制器的视图出现时,它会发送一个REST查询(当它异步返回时,它会在fromDoc:
方法中更新模型;控制器有KVO
个关系设置将导致UI更改。更新完成后,它能够估计何时应该进行下一次拉动,并使用performSelector:withObject:afterDelay:
进行计划。当另一个控制器占据中心位置时,{{ 1}}方法尝试停止已排队的任何viewWillDisappear:
。
虽然这种方法有效。我很确定它没有通过"做对了"测试。我对所有任务和后台工作的工作方式都很天真,但在我看来,forkPull
会增加自己的级别,因此我的AFNetworking
可能无效。我已经在我的stopPull
输出中看到了一些证据,看起来控制器不再位于顶部,仍然有循环运行。
但我确定其他人之前已经做过这种模式。我很想知道如何更好地构建/实现它。我正在寻找某人分享他们用于执行半周期REST查询的模式,这些模式已经过审查并且运行良好。
答案 0 :(得分:6)
使用Grand Central Dispatch:
@property (strong, nonatomic) dispatch_source_t timer;
- (void)startTimer
{
if (!self.timer) {
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
}
if (self.timer) {
dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), 60ull*NSEC_PER_SEC, 10ull*NSEC_PER_SEC);
dispatch_source_set_event_handler(_timer, ^(void) {
[self tick];
});
dispatch_resume(_timer);
}
}
- (void)tick
{
// Do your REST query here
}
这将每60秒调用一次tick
方法。
要暂停和恢复计时器,请使用dispatch_suspend和dispatch_resume:
dispatch_suspend(self.timer);
dispatch_resume(self.timer);
您可以在以后随时调用dispatch_source_set_timer
以更快地安排滴答或将其推迟到以后:
// Fire sooner than 60 seconds, but resume 60s fires after that
unsigned long long delaySeconds = arc4random() % 60;
dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, delaySeconds * NSEC_PER_SEC), 60ull*NSEC_PER_SEC, 10ull*NSEC_PER_SEC);
有关此问题的完整文档,请参阅Apple Concurrency Programming Guide。