【问题标题】:Thread Handling with NSURLSessionDataTask使用 NSURLSessionDataTask 进行线程处理
【发布时间】:2020-02-04 04:03:15
【问题描述】:

简而言之,我试图在 WEB 上显示来自公开可用 JSON 文件的数据。流程如下:

我使用NSURLSessionDataTask 启动下载,然后解析并显示 JSON 或处理错误(如果发生)。这是我的相关代码:

- (void) initiateDownload {
    NSURLSessionConfiguration *sessionConfig    = [NSURLSessionConfiguration defaultSessionConfiguration];
    sessionConfig.timeoutIntervalForRequest     = 5.0f;
    sessionConfig.timeoutIntervalForResource    = 20.0f;

    NSURLSessionDataTask *downloadWeatherTask   = [urlSession dataTaskWithRequest:urlRequest
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *downloadError) {
                                                if (downloadError) {
    dispatch_sync(dispatch_get_main_queue(), ^{
                                                        [self errorReceived:downloadError];
                                                        });
                                                } else if (data) {
    dispatch_sync(dispatch_get_main_queue(), ^{
                                                        [self parseWeatherJSON:data];
                                                        });
                                                }
                                            }];

    [downloadWeatherTask resume];
}

我对此有几个问题:

  1. 我对线程处理不是很熟悉。虽然我添加了

    dispatch_sync(dispatch_get_main_queue(), ...)
    

    到两个完成块,它似乎工作,我不确定这是线程安全的最佳方式(之前,我收到各种错误消息并在下载完成后显示数据需要 10 秒)。有没有更好的方法来处理下载,线程或我的是可接受的解决方案?

  2. 我希望用户能够在他/她想要刷新显示的数据的任何时候手动启动下载过程。起初,我将NSURLSessionDataTask 初始化一次,并使其在课堂上的任何地方都可用;所以我可以在每次调用刷新时调用 resume。但是,我找不到重新执行下载过程的命令。一旦我打电话给[downloadWeatherTask resume],我就无法从头开始任务。

    所以,我将任务添加到一个单独的函数(您在上面看到的那个),并在每次调用该函数时对其进行初始化。这很好用,但我不确定这是最好的方法。例如,它是内存安全的,还是我每次用户发起刷新调用时都创建一个新任务并最终耗尽内存?

感谢您的回答!

更多信息:我使用最新的 XCode 11 并针对 iOS 9 及更高版本。

【问题讨论】:

  • 如果你原谅了不相关的观察,但你正在创建一个NSURLSessionConfiguration,但你没有使用它。配置应在NSURLSession 的实例化期间创建和使用。而且您不想在每次执行此请求时都这样做,因此请将此 NSURLSessionConfiguration 代码从此例程中拉出,并将其放在您实例化 urlSession 的任何位置。
  • 另外,一个非常小的观察,但是您使用“下载任务”的术语来指代您的NSURLSessionDataTask。实际上有一个名为NSURLSessionDownloadTask 的对象类型,用于下载到文件。您可以考虑为您在此处所做的工作采用“数据任务”这一术语,以避免将来出现混淆。
  • "如果你原谅了不相关的观察,但你正在创建一个 NSURLSessionConfiguration,但你没有使用它。"谢谢你指出。我的印象是它是全球性的,并且普遍适用于所有 url 会话。那么,我如何使它在这种情况下正常工作?如果我只是将它移动到我的 init 会发生什么(那是我初始化我的 url 会话的地方)。
  • 不,这不是全球性的。无论您定义urlSession,您都可以使用use this configuration,例如urlSession = [NSURLSession sessionWithConfiguration:sessionConfig];。但请确保在init 中执行此操作,因此您只创建一次会话。每个 NSURLSession 都有开销。
  • 谢谢 Rob,这很有道理。

标签: objective-c ios9 nsurlsession ios13 xcode11


【解决方案1】:

NSURLSession 默认情况下会为完成块使用专用的后台串行队列(以及委托方法,如果你这样做的话)。但您确实希望确保从主队列触发 UI 更新(通过 dispatch_get_main_queue() 检索)。而且您通常希望避免从多个线程更新属性和 ivars(除非它们内置了一些线程安全,这是不寻常的),因此将这些属性/ivars 的更新分派回主队列是一个很好的简单实现线程安全的方法。

所以,归根结底,你在这里做的很好。

但是,我找不到重新执行下载过程的命令。

您只执行 (resume) 给定任务一次。如果您想再次执行类似的请求,请实例化一个新的NSURLSessionDataTask

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-23
    • 1970-01-01
    • 2019-02-27
    • 2018-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多