如何正确调用GrandCentralDispatch



我正在尝试从API加载自定义数据类型,并将其显示在iOS应用程序的登录页上。

据我所知,我应该称之为:

override func viewDidLoad() {
performSelector(inBackground: #selector(fetchJSON), with: nil)
let myCustomDataType = //how so I get this back
tableView.reloadData()
//...
}

显然,fetchJSON的声明应该在另一个文件中,这样我就不会用不需要做的事情来填充我的控制器。然而,我需要这个函数来返回[MyCustomDataType]的列表,并在我的登录页上显示这些列表。

func fetchJSON() -> [MyCustomDataType] {
//get api, fetch data, put it in my custom data type list
return myCustomDataType
}

我使用闭包吗?创建全局[MyCystomDataType]。或者我该如何做到这一点?注意任务是异步的,因为我显示的表格单元格有点像facebook或instagram新闻订阅页面。

如果fetchJSON异步加载数据,则必须添加一个完成处理程序

func fetchJSON(completion: @escaping ([MyCustomDataType]) -> Void){
API.fetch { fetchedData in
completion(fetchedData)
}
}

然后在后台线程上调用fetchJSON,并在主线程上重新加载表视图

override func viewDidLoad() {
DispatchQueue.global().async {
self.fetchJSON { data in 
DispatchQueue.main.async {
self.myCustomDataType = data
self.tableView.reloadData()
}
}
}
}

流行的异步API(如URLSessionAlamofire(在后台线程上隐式地执行网络请求。如果要使用其中一个,则可以省略第一个async块。

performSelector是一个过时的objective-c-ishAPI。不要在Swift 中使用

performSelector(inBackground:)不是GCD。这是OSX10.5中的一个预GCD方法,您几乎永远不应该使用它。(自从10.6引入GCD以来,我想我没有任何理由使用它。(

通常,对于网络请求,您根本不需要直接使用GCD。URLSessionTask已经是异步的。有关更多信息,请参阅将网站数据提取到内存中。如图所示,您通常需要使用GCD(DispatchQueue.main.async(数据返回到UI,但不需要它来启动请求。

但对于你的基本问题,答案是,到viewDidLoad完成时,你还没有数据。你需要能够处理没有数据的问题,并适当地绘制你的UI。当数据显示时,您可以更新UI。

正如Vadian所说(+1(,您希望使用异步模式,而使用闭包是一个很好的模式。但问题不仅在于如何返回数据,还在于在出现错误的情况下,如何报告Error。我们经常使用Result<Success, Failure>模式来实现这一点。例如,它可能具有Result<[MyCustomDataType], Error>类型,而不是[MyCustomDataType]参数。

例如,假设您正在通过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更新即可。

相关内容

  • 没有找到相关文章

最新更新