如何正确调用 Grand Central Dispatch
How to call GrandCentralDispatch properly
我正在尝试从我的 API 加载自定义数据类型并将其显示在我的 iOS 应用程序的登录页面上。
据我所知,我应该这样调用:
override func viewDidLoad() {
performSelector(inBackground: #selector(fetchJSON), with: nil)
let myCustomDataType = //how so I get this back
tableView.reloadData()
//...
}
显然 fetchJSON 的声明应该在另一个文件中,这样我就不会用它不需要做的事情来填满我的控制器。但是我需要这个函数来 return [MyCustomDataType] 的列表并将它们显示在我的登陆页面上。
func fetchJSON() -> [MyCustomDataType] {
//get api, fetch data, put it in my custom data type list
return myCustomDataType
}
我使用闭包吗?创建一个全局 [MyCystomDataType]。或者我如何实现这一目标?
请注意任务是异步的,因为我显示 table 个单元格有点像 facebook 或 instagram 新闻提要页面。
performSelector(inBackground:)
不是 GCD。这是来自 OS X 10.5 的 GCD 之前的方法,您几乎不应该使用它。 (自从 10.6 引入 GCD 以来,我认为我没有理由使用它。)
一般来说,对于网络请求,您根本不需要直接使用 GCD。 URLSessionTask 已经是异步的。有关更多信息,请参阅 Fetching Website data into memory。如那里所示,您经常需要使用 GCD (DispatchQueue.main.async
) 将 return 数据传送到 UI,但您并不需要它启动请求。
但对于您的基本问题,答案是到 viewDidLoad
完成时,您还没有数据。您需要能够处理没有数据的情况,并适当地绘制您的 UI。当数据出现时,您可以更新 UI.
如果 fetchJSON
异步加载数据,您必须添加完成处理程序
func fetchJSON(completion: @escaping ([MyCustomDataType]) -> Void){
API.fetch { fetchedData in
completion(fetchedData)
}
}
然后在后台线程上调用 fetchJSON
并在主线程上重新加载 table 视图
override func viewDidLoad() {
DispatchQueue.global().async {
self.fetchJSON { data in
DispatchQueue.main.async {
self.myCustomDataType = data
self.tableView.reloadData()
}
}
}
}
流行的异步 API 像 URLSession
和 Alamofire
隐式地在后台线程上执行它们的网络请求。如果你打算使用其中一个,你可以省略第一个 async
块。
performSelector
是一个过时的 objective-c-ish API。不要在 Swift
中使用它
正如 Vadian 所说 (+1),您想使用异步模式,而使用闭包是一个很好的模式。但问题不仅仅是如何交回数据,而是在出错的情况下,如何报告Error
。我们经常使用 Result<Success, Failure>
模式来实现这一点。例如。而不是 [MyCustomDataType]
参数,它可能具有 Result<[MyCustomDataType], Error>
类型。
例如,假设您正在通过 URLSession
执行您的请求。那么你可能会有这样的例程:
enum ApiError: Error {
case networkError(Data?, URLResponse?)
}
func fetchJSON<T: Decodable>(_ request: URLRequest, queue: DispatchQueue = .main, completion: @escaping (Result<T, Error>) -> Void) {
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// detect and report error
guard
error == nil,
let responseData = data,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~= httpResponse.statusCode
else {
queue.async {
completion(.failure(error ?? ApiError.networkError(data, response)))
}
return
}
// no network error, so let's parse the response
do {
let responseObject = try JSONDecoder().decode(T.self, from: responseData)
queue.async {
completion(.success(responseObject))
}
} catch let parseError {
queue.async {
completion(.failure(parseError))
}
}
}
task.resume()
}
并执行特定请求:
func fetchMyCustomDataTypes(completion: @escaping (Result<[MyCustomDataType], Error>) -> Void) {
let request: URLRequest = ... // build your request here
fetchJSON(request) { result in
completion(result)
}
}
现在,不要太迷失在上面的细节中,因为您的实现可能会有所不同(例如,您可以轻松地使用 Alamofire 或其他)。关键是我们有一个包含 Swift.Result
参数的完成处理程序闭包,通过该参数可以将数据或错误信息提供给调用者。
现在,调用者可以根据结果是 success
还是 failure
采取相应行动:
override func viewDidLoad() {
super.viewDidLoad() // make sure to call `super`
fetchMyCustomDataTypes { result in
switch result {
case .failure(let error):
// handle error here, e.g., report the error in the UI; or at the minimum, during development, just print the error
print(error)
case .success(let objects):
// use the `[MyCustomDataType]` array, `objects` here, e.g.
self.objects = objects
self.tableView.reloadData()
}
}
}
但是来电者没有使用performSelector
。它也不需要将此网络请求分派到后台队列,因为网络请求本质上已经是异步的。只需调用 fetchJSON
并指定完成处理程序中必须发生的 UI 更新。
我正在尝试从我的 API 加载自定义数据类型并将其显示在我的 iOS 应用程序的登录页面上。
据我所知,我应该这样调用:
override func viewDidLoad() {
performSelector(inBackground: #selector(fetchJSON), with: nil)
let myCustomDataType = //how so I get this back
tableView.reloadData()
//...
}
显然 fetchJSON 的声明应该在另一个文件中,这样我就不会用它不需要做的事情来填满我的控制器。但是我需要这个函数来 return [MyCustomDataType] 的列表并将它们显示在我的登陆页面上。
func fetchJSON() -> [MyCustomDataType] {
//get api, fetch data, put it in my custom data type list
return myCustomDataType
}
我使用闭包吗?创建一个全局 [MyCystomDataType]。或者我如何实现这一目标? 请注意任务是异步的,因为我显示 table 个单元格有点像 facebook 或 instagram 新闻提要页面。
performSelector(inBackground:)
不是 GCD。这是来自 OS X 10.5 的 GCD 之前的方法,您几乎不应该使用它。 (自从 10.6 引入 GCD 以来,我认为我没有理由使用它。)
一般来说,对于网络请求,您根本不需要直接使用 GCD。 URLSessionTask 已经是异步的。有关更多信息,请参阅 Fetching Website data into memory。如那里所示,您经常需要使用 GCD (DispatchQueue.main.async
) 将 return 数据传送到 UI,但您并不需要它启动请求。
但对于您的基本问题,答案是到 viewDidLoad
完成时,您还没有数据。您需要能够处理没有数据的情况,并适当地绘制您的 UI。当数据出现时,您可以更新 UI.
如果 fetchJSON
异步加载数据,您必须添加完成处理程序
func fetchJSON(completion: @escaping ([MyCustomDataType]) -> Void){
API.fetch { fetchedData in
completion(fetchedData)
}
}
然后在后台线程上调用 fetchJSON
并在主线程上重新加载 table 视图
override func viewDidLoad() {
DispatchQueue.global().async {
self.fetchJSON { data in
DispatchQueue.main.async {
self.myCustomDataType = data
self.tableView.reloadData()
}
}
}
}
流行的异步 API 像 URLSession
和 Alamofire
隐式地在后台线程上执行它们的网络请求。如果你打算使用其中一个,你可以省略第一个 async
块。
performSelector
是一个过时的 objective-c-ish API。不要在 Swift
正如 Vadian 所说 (+1),您想使用异步模式,而使用闭包是一个很好的模式。但问题不仅仅是如何交回数据,而是在出错的情况下,如何报告Error
。我们经常使用 Result<Success, Failure>
模式来实现这一点。例如。而不是 [MyCustomDataType]
参数,它可能具有 Result<[MyCustomDataType], Error>
类型。
例如,假设您正在通过 URLSession
执行您的请求。那么你可能会有这样的例程:
enum ApiError: Error {
case networkError(Data?, URLResponse?)
}
func fetchJSON<T: Decodable>(_ request: URLRequest, queue: DispatchQueue = .main, completion: @escaping (Result<T, Error>) -> Void) {
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// detect and report error
guard
error == nil,
let responseData = data,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~= httpResponse.statusCode
else {
queue.async {
completion(.failure(error ?? ApiError.networkError(data, response)))
}
return
}
// no network error, so let's parse the response
do {
let responseObject = try JSONDecoder().decode(T.self, from: responseData)
queue.async {
completion(.success(responseObject))
}
} catch let parseError {
queue.async {
completion(.failure(parseError))
}
}
}
task.resume()
}
并执行特定请求:
func fetchMyCustomDataTypes(completion: @escaping (Result<[MyCustomDataType], Error>) -> Void) {
let request: URLRequest = ... // build your request here
fetchJSON(request) { result in
completion(result)
}
}
现在,不要太迷失在上面的细节中,因为您的实现可能会有所不同(例如,您可以轻松地使用 Alamofire 或其他)。关键是我们有一个包含 Swift.Result
参数的完成处理程序闭包,通过该参数可以将数据或错误信息提供给调用者。
现在,调用者可以根据结果是 success
还是 failure
采取相应行动:
override func viewDidLoad() {
super.viewDidLoad() // make sure to call `super`
fetchMyCustomDataTypes { result in
switch result {
case .failure(let error):
// handle error here, e.g., report the error in the UI; or at the minimum, during development, just print the error
print(error)
case .success(let objects):
// use the `[MyCustomDataType]` array, `objects` here, e.g.
self.objects = objects
self.tableView.reloadData()
}
}
}
但是来电者没有使用performSelector
。它也不需要将此网络请求分派到后台队列,因为网络请求本质上已经是异步的。只需调用 fetchJSON
并指定完成处理程序中必须发生的 UI 更新。