使用 NSURLSessionDataTask 处理线程
Thread Handling with NSURLSessionDataTask
简而言之,我试图显示来自 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];
}
我对此有几个问题:
我对线程处理不是很熟悉。虽然我添加了
dispatch_sync(dispatch_get_main_queue(), ...)
两个完成块,它似乎工作,我不确定这是线程安全的最佳方法(之前,我收到各种错误消息,下载完成后显示数据需要 10 秒已经完成了)。有没有更好的方法来处理下载和线程或我的是可接受的解决方案?
我希望用户能够随时手动启动下载过程he/she想要刷新显示的数据。起初,我初始化了 NSURLSessionDataTask
一次并使其在 class 中的任何地方都可用;所以我可以在每次调用刷新时调用 resume 。但是,我找不到重新执行下载过程的命令。一旦我调用[downloadWeatherTask resume]
,我无法从头开始任务。
因此,我将任务添加到一个单独的函数(您在上面看到的那个)并在每次调用该函数时都在那里初始化它。这很好用,但我不确定这是最好的方法。例如,它是内存安全的还是每次用户发起刷新调用时我都会创建一个新任务并最终 运行 内存不足?
感谢您的回答!
更多信息:我使用最新的 XCode 11,目标是 iOS 9 及更高版本。
默认情况下,NSURLSession
将使用专用的后台串行队列来完成块(以及委托方法,如果你这样做的话)。但是您真的想确保从主队列(通过 dispatch_get_main_queue()
检索)触发 UI 更新。而且您通常希望避免从多个线程更新属性和 ivar(除非它们内置了一些线程安全性,这是不常见的),因此将更新分派给那些 properties/ivars 回到主队列是一个实现线程安全的好简单方法。
所以,最重要的是,你在这里做的很好。
However, I could not find a command to re-do the download process.
您只执行 (resume
) 一次给定任务。如果你想再次执行类似的请求,实例化一个新的NSURLSessionDataTask
.
简而言之,我试图显示来自 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];
}
我对此有几个问题:
我对线程处理不是很熟悉。虽然我添加了
dispatch_sync(dispatch_get_main_queue(), ...)
两个完成块,它似乎工作,我不确定这是线程安全的最佳方法(之前,我收到各种错误消息,下载完成后显示数据需要 10 秒已经完成了)。有没有更好的方法来处理下载和线程或我的是可接受的解决方案?
我希望用户能够随时手动启动下载过程he/she想要刷新显示的数据。起初,我初始化了
NSURLSessionDataTask
一次并使其在 class 中的任何地方都可用;所以我可以在每次调用刷新时调用 resume 。但是,我找不到重新执行下载过程的命令。一旦我调用[downloadWeatherTask resume]
,我无法从头开始任务。因此,我将任务添加到一个单独的函数(您在上面看到的那个)并在每次调用该函数时都在那里初始化它。这很好用,但我不确定这是最好的方法。例如,它是内存安全的还是每次用户发起刷新调用时我都会创建一个新任务并最终 运行 内存不足?
感谢您的回答!
更多信息:我使用最新的 XCode 11,目标是 iOS 9 及更高版本。
NSURLSession
将使用专用的后台串行队列来完成块(以及委托方法,如果你这样做的话)。但是您真的想确保从主队列(通过 dispatch_get_main_queue()
检索)触发 UI 更新。而且您通常希望避免从多个线程更新属性和 ivar(除非它们内置了一些线程安全性,这是不常见的),因此将更新分派给那些 properties/ivars 回到主队列是一个实现线程安全的好简单方法。
所以,最重要的是,你在这里做的很好。
However, I could not find a command to re-do the download process.
您只执行 (resume
) 一次给定任务。如果你想再次执行类似的请求,实例化一个新的NSURLSessionDataTask
.