我可以在不使用NSTimer的情况下使用连续后台线程更新iPhone UI吗?

我可以在不使用NSTimer的情况下使用连续后台线程更新iPhone UI吗?,iphone,objective-c,architecture,multithreading,Iphone,Objective C,Architecture,Multithreading,比如说,如果我从www.example.com/number上阅读,我会得到一个随机数。在我的iPhone应用程序中,我希望能够连续读取该地址,并在每次请求完成后在屏幕上显示新号码。我们还假设我希望这个过程在视图加载后立即开始。最后,作为旁注,我将使用简化web请求 方法1:在viewDidLoad方法中,我可以在一个循环中同步读取URL(在收到HTTP请求的响应之前,执行不会继续)。优点:请求是串行的,我可以完全控制对每个请求的响应。缺点:UI从未更新,因为我从未退出函数并将控制权交还给运行时

比如说,如果我从www.example.com/number上阅读,我会得到一个随机数。在我的iPhone应用程序中,我希望能够连续读取该地址,并在每次请求完成后在屏幕上显示新号码。我们还假设我希望这个过程在视图加载后立即开始。最后,作为旁注,我将使用简化web请求

方法1:在viewDidLoad方法中,我可以在一个循环中同步读取URL(在收到HTTP请求的响应之前,执行不会继续)。优点:请求是串行的,我可以完全控制对每个请求的响应。缺点:UI从未更新,因为我从未退出函数并将控制权交还给运行时循环。显然,这不是一个好的解决方案

方法2:在viewDidLoad方法中,我创建了一个计时器,它每秒调用一次fetchURL函数。优点:每个请求都在一个单独的线程中,每个请求完成后UI都会更新。缺点:请求在单独的线程中,无法很好地控制。例如,如果在第一次请求时存在连接超时,我希望能够显示错误弹出窗口,并且在更改设置之前不会发生任何进一步的请求。但是,使用这种方法,如果超时需要3秒钟,那么在这段时间内,将已经启动了另外两个请求。如果我只是放慢了定时器的速度,那么当连接正常工作时,数据进入的速度就太慢了

似乎应该有某种方法将我提到的前两种方法的优点结合起来。我想要一种方式,我可以决定是否不发送下一个请求的基础上的结果,前一个请求

方法3:我考虑使用一个触发更快的计时器(比如说每.25秒),但是让计时器的功能检查一个标志,看看下一步该怎么做。因此,如果前一个请求已完成,它将发送一个新请求(除非有错误)。否则,如果前一个请求尚未完成,计时器的函数将返回,而不发送新的请求。通过更快地启动此计时器,您将获得更好的响应时间,但该标志将让我获得所需的同步


看起来方法3可以满足我的要求,但也有点强迫。有没有人对此有更好的方法的建议,或者类似于方法3的方法是最好的方法?

我相信NSOperation是您所需要的。使用上面的1号解决方案,但将代码放在NSO操作的主方法中。大概是这样的:

.h文件

@interface MyRandomNumberFetcher : NSOperation {

}

@end
.m文件

@implementation MyRandomNumberFetcher

- (void) main {
      // This is where you start the web service calls.
}

@end

我还建议添加对UI控制器的引用,以便您的操作队列类可以在适当的时候调用它。

我相信NSOperation就是您所需要的。使用上面的1号解决方案,但将代码放在NSO操作的主方法中。大概是这样的:

.h文件

@interface MyRandomNumberFetcher : NSOperation {

}

@end
.m文件

@implementation MyRandomNumberFetcher

- (void) main {
      // This is where you start the web service calls.
}

@end

我还建议添加对UI控制器的引用,以便您的操作队列类可以在适当的时候调用它。

这里还有另一个建议。创建一个NSOperationQueue,它将在不同的线程上运行您的请求。如果发现需要刷新UI,请调用performSelectorOnMainThread。请求完成后,创建另一个请求并将其添加到队列中。将队列设置为一次只运行一个操作


这样,您就不会同时运行两个请求。

这里还有另一个建议。创建一个NSOperationQueue,它将在不同的线程上运行您的请求。如果发现需要刷新UI,请调用performSelectorOnMainThread。请求完成后,创建另一个请求并将其添加到队列中。将队列设置为一次只运行一个操作


这样你就不会有两个请求同时运行。

你可以用更少的代码和资源使用GCD来实现这一点。这就是你可以做到的:

viewDidLoad
中,异步调用执行以下操作的块(使用
dispatch\u async
):

  • 使用同步调用加载数据,并在失败时处理超时
  • 如果成功,通知主线程更新UI
  • 将一个新块排队,以便在执行相同操作的延迟后运行(使用
    dispatch\u after
  • 要从另一个线程调用主线程,我可以想到以下方法:

  • 如果要更新自定义视图,可以从块中设置
    setNeedsDisplay
  • 否则,您可以在所谓的“主队列”上对块进行排队,这是一个在主线程上运行的队列。您可以通过调用
    dispatch\u get\u main\u queue
    来获取此队列。然后将其视为任何其他队列(例如,您可以通过调用
    dispatch\u async
    )来添加块)
  • 如果不想使用块,可以使用
    NSObject
    performselectornmainthread:withObject:waituntldone:
    方法
  • 有关更多详细信息,请参阅


    这就是说,您永远不应该如此频繁地执行小请求(除非用于获取游戏数据之类的特定任务)。它将通过阻止天线休眠来严重缩短电池寿命。

    您可以使用GCD以更少的代码和更少的资源来实现这一点。这就是你可以做到的:

    viewDidLoad
    中,异步调用执行以下操作的块(使用
    dispatch\u async
    ):

  • 使用同步调用加载数据,并在失败时处理超时
  • 如果成功,通知主线程更新UI
  • 将一个新块排队,以便在执行相同操作的延迟后运行(使用
    dispatch\u after
  • 要从另一个线程调用主线程,我可以想到以下方法:

  • 如果要更新自定义视图,可以从块中设置
    setNeedsDisplay
  • Ot