使用GCD或NSThread进行iOS长时间运行?

时间:2014-04-17 21:10:56

标签: ios objective-c multithreading

我是iOS开发的新手(我最熟悉Java),我想知道启动长时间运行线程的最佳方法是什么?只要应用程序位于前台,该线程就会启动,并在应用程序进入后台时停止。当它在前台时,它将每隔X秒轮询一次外部设备,看它是否连接。

NSThread与Java线程类非常相似,这让我很容易理解。我知道我只能initWithTarget:selector:object:该类并调用start:并且它将使用选择器中提供的方法启动线程。在那里,我只有一个while(true)循环,一直运行,直到我突破它。以下是我要做的事情的基本示例:

- (void)startup {
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(devicePoller) object:nil];
    [thread start];
}

- (void)devicePoller {
    while (self.started) {
        if (![self.device isConnected]) {
            //notify the user the device isn't connected
        }

        [NSThread sleepForTimeInterval:2];
    }
}

但是,我看到人们建议使用GCD,因为它具有更好的性能。我理解如何使用dispatch_async()异步执行代码,但所有示例似乎都是针对单个长时间运行的操作,而不是在应用程序运行的整个时间运行的操作。有没有办法用GCD(我应该)或其他方式做到这一点?

3 个答案:

答案 0 :(得分:3)

如果您需要轮询并且想要在后台线程上执行此操作,我可能会建议调度计时器:

@property (nonatomic, strong) dispatch_source_t timer;

然后您可以将此计时器配置为每两秒触发一次:

dispatch_queue_t queue = dispatch_queue_create("com.domain.app.devicepoller", 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), 2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(self.timer, ^{
    [self pollDevice];    // this method should just poll the device and then return; no loop needed in this method
});
dispatch_resume(self.timer);

请参阅并发编程指南中对dispatch sources的讨论。另请参阅Grand Central Dispatch (GCD) Reference

但是大多数设备都支持某种形式的事件驱动通知,不需要像这样进行任何轮询,但是如果你必须进行轮询,并且如果你想在主线程上进行,那么这是一种方法。


当您希望计时器在后台队列上运行时,调度计时器非常有用。如果供应商的API响应足以在主队列中无缝运行,则使用NSTimer方法。它使您不必进行大量额外工作以使代码具有线程安全性(例如,在更新模型对象时正确synchronization等)。我假设从你原来的问题的基于线程的方法,你有一些理由不使用计时器,你不得不出于某种原因将其移动到另一个线程,在这种情况下,我认为调度计时器可能是一个更好的方法来处理它而不是使用永久NSThread循环进行while编程。但是如果你(a)必须进行民意调查; (b)没有迫切需要编写多线程代码,如果你使用基于NSTimer的方法,那么你的生活可能会更简单。

就个人而言,我确定在追求计时器之前我已经用尽了Core Bluetooth方法。物理设备的应用级轮询应被视为最后的手段。也许您可以联系API的供应商,看看他们是否有除轮询之外的建议(因为如果他们已经这样做了一段时间,他们可能会有更优雅的解决方案,他们可以建议)。

关于接收Web服务更新,再次轮询非常低效(并且根据您的轮询频率,它可能会对电池寿命产生负面影响,消耗蜂窝数据计划等)。如果服务器数据不经常更改,您可能需要Push notifications,但您希望主动通知您的应用更改。或者如果服务器真的在不断变化,那么也许一些基于套接字的方法可能有意义。但是每两秒轮询一次网络资源(如果这是你的意思)很少是正确的方法。

就像轮询物理设备是最后的架构一样,对于网络通信来说更是如此。您真的想要提出一种能够根据业务需求平衡设备注意事项的架构。如果您得出结论,您必须采用基于轮询的方法,也许您可​​能会考虑采用不同的轮询频率,具体取决于用户是使用wifi还是基于蜂窝。

答案 1 :(得分:2)

NSThread / NSTimer与GCD操作/定时器之间的区别很简单:

NSThread / NSTimer大约25岁,表现出过去25年中任何人都期望他们表现的方式。

GCD是一种先进的技术,可以达到极高的性能水平和低电池消耗,使用传统方法无法实现。

这些天通常应避免使用NSThread和NSTimer。除了向后兼容性和熟悉程度外,它们在各方面都比GCD差。

如果您正在寻找GCD的高级API,请使用NSOperationQueue。

请注意,GCD正在使用线程。但它会智能地使用它们,考虑到内核知道硬件以及设备上运行的其他软件的信息,这些信息都不能在您自己的应用程序中执行。这比使用NSThread实现的性能要好得多。

根据blog post from four years ago(几乎肯定是过时的,并且在与现代iOS设备完全不同的硬件上执行),使用NSThread的6秒操作使用GCD需要0.05秒。它们都使用线程,但GCD更智能地使用它们。

答案 2 :(得分:1)

没有理由使用线程或GCD队列。只需使用NSTimerdispatch_after()定期检查设备是否仍然连接。

然而,此设计从根本上与推荐的模式相反。你应该从不民意调查,除非你绝对必须这样做。

您正在监控哪种外部设备?设备断开连接时是否真的没有发送任何类型的通知?

通常,您会在应用启动时以及应用程序到达前台时检查连接的可行性。在正常的应用程序操作期间,只要连接状态发生变化,您就会监视发送的任何通知。