3

我的要求是我想每 6 秒调用一次 API 从我的服务器请求一些新信息,所以我编写了如下代码:

MyBackgroundThread(){

    while(self.isStop){
        [self callMyAPI]; 
        [NSThread sleepfortimeinterval : 6 ];
    }
}

但是我今天发现Foundation库提供了一种编写运行循环的方法。所以我可以重写我的代码如下:

MyBackgroundThread(){

    NSTimer *timer = [NSTimer timerWithTimeInterval:6 target:self selector:@selector(callMyAPI) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    [timer release];
    while (! self.isCancelled) {
        BOOL ret = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }
}

但是,我不知道这些是不是比原来的更好的方法来完成我的工作?如果是,为什么?以及如何测试这两种方式之间的效率(或其他属性?)差异?

谢谢!

4

1 回答 1

3

我认为通常没有必要为计时器创建新的运行循环。我建议以下两种方法之一:

  1. 在主运行循环上调度NSTimer,但让被调用的方法将请求分派到后台队列。

  2. 创建调度计时器,调度在指定的后台调度队列上运行。为此,请创建调度计时器属性:

    @property (nonatomic, strong) dispatch_source_t timer;
    

    然后实例化并启动调度计时器源以在您指定的 GCD 队列上运行:

    dispatch_queue_t queue = dispatch_queue_create("com.domain.app.polltimer", 0);
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), kPollFrequencySeconds * NSEC_PER_SEC, 1ull * NSEC_PER_SEC);
    
    dispatch_source_set_event_handler(self.timer, ^{
        <#code to be run upon timer event#>
    });
    
    dispatch_resume(self.timer);
    

有时创建一个新的运行循环很有用,但在这个简单的场景中似乎没有必要。


话虽如此,使用计时器每六秒启动一次网络可能没有意义。相反,您可能希望在前一个请求完成六秒后开始下一个请求由于各种原因,您的服务器可能无法在 6 秒内响应,并且您不希望在这些情况下建立并发请求(如果您的请求异步运行,可能会发生这种情况)。

所以,我倾向于callMyAPI做一些简单的事情的完成块:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6.0 * NSEC_PER_SEC)), queue, ^{
    <#code to issue next request#>
});

这完全消除了对计时器(和自定义运行循环)的需求。


最后,如果您确实需要以该频率检测系统更改,则可能建议使用非常不同的服务器架构。例如,如果您每六秒轮询一次以查看服务器上是否发生了变化,您可能会考虑使用基于套接字的实现或使用推送通知。在这两种方法中,当重大事件发生时,服务器会告诉客户端应用程序,而不是像 Bart Simpson 那样在汽车后座上不断询问“我们到了吗?”的应用程序。

适当的架构可能取决于服务器数据可能更改的频率以及客户端应用程序的要求。

于 2014-10-19T16:05:27.630 回答