最近,我一直致力于在功能从Web请求获取所需信息时需要更新UI的内容。
我发现在这个函数中传入一个空闭包然后在同一个函数中调用闭包允许我在下载数据后更新UI(在我尝试更新UI之前没有闭包和程序崩溃,因为数据仍在下载中。)
首先我创建了一个类型别名:
typealias DLComplete = () -> ()
这是函数的样子:
func DLDetails(completed: DLComplete) {
let url = "string"
Alamofire.request(GET, url).responseJSON { response in
//Getting all the data I need and putting them in variables
} completed()
然后在我的视图控制器中:
ViewDidLoad() {
super.viewdidload()
DLDetails() {
//call function that updates UI
}
}
所以基本上,我想知道,为什么创建一个这样的空闭包允许程序首先下载数据,一旦下载数据,然后更新UI。一切如何运作?
如何在我的DLDetails
函数中调用空闭包,允许我在我的VC中调用此函数,打开另一个闭包,让我调用更新UI函数?
我不熟悉闭包,所以我甚至不确定() -> ()
如何允许我在下载数据后调用视图控制器中的更新UI功能。
答案 0 :(得分:2)
你说:
我无法理解
() -> ()
正在做什么或(String?, NSError?) -> ()
正在做什么。
这些结构本身并不是做任何事情。它只是定义了一个closure,一段可以从一种方法传递到另一种方法的代码。在这种情况下,想法是viewDidLoad
可以说"启动一些异步网络请求,继续并立即返回,以便应用程序不会阻止主线程(即不会冻结用户界面),但这是你可以在完成后调用的一段代码,这样我就可以在异步请求完成时更新我的UI。"
所以,() -> ()
说这个变量将保持一个闭包。在() -> ()
中,viewDidLoad
提供的闭包被定义为不带参数且不返回任何值的闭包。在(String?, NSError?) -> ()
中,它说闭包将传递两个参数,可选字符串和错误引用,但不返回任何值。简而言之,它为下载方法提供了在请求成功时传回字符串值的机会,或者在请求失败时传递错误对象的机会。因此,viewDidLoad
提供了闭包,下载方法负责在异步请求完成时调用闭包。
你问:
我想知道,为什么创建这样的空闭包允许程序首先下载数据,一旦下载数据,然后更新UI。一切如何运作?
所有涉及异步方法的时间问题。 Alamofire的responseJSON
方法立即返回,但其尾随闭包是异步调用的(即稍后,在请求完成后)。因此,如果要在视图控制器中触发UI更新,则在DLDetails
方法中采用自己的完成处理程序模式,并且仅在调用responseJSON
完成处理程序时调用其完成处理程序。
顺便说一句,在您的Alamofire示例中,请务必将completed()
放在<{1}}关闭内,而不是在代码段中显示之后。我们的想法是在请求完成时调用您的闭包,如果您没有将它放在responseJSON
闭包内,它将在请求完成之前提前调用。
您可能考虑不直接在responseJSON
内更新模型,而是定义DLDetails
以传回检索到的数据。例如,如果返回字符串,则completed
将为DLComplete
(例如,如果请求成功则传递(String?) -> ()
,否则返回String
。您也可以传回nil
或ErrorType
引用,因此如果出现错误,视图控制器有机会显示适合特定类型错误的UI(例如,身份验证错误)可能会触发重新身份验证流,而网络连接错误可能会触发不同的UI)。
NSError
然后在你的视图控制器中:
typealias DownloadCompletion = (String?, NSError?) -> ()
func downloadDetails(completionHandler: DownloadCompletion) {
let url = "string"
Alamofire.request(.GET, url)
.validate()
.responseJSON { response in
switch response.result {
case .Failure(let error):
// handle errors (including `validate` errors) here
print(error)
completionHandler(nil, error)
case .Success(let value):
// handle success here, retrieving/parsing the necessary value(s)
let string = ...
completionHandler(string, nil)
}
}
}
显然,override func viewDidLoad() {
super.viewDidLoad()
downloadDetails() { responseString, error in
guard let string = responseString else {
// handle failure here
return
}
// do something with `string` here, e.g update model and/or UI
}
// But don't try to use `responseString` or `error` here, because the above
// closure runs asynchronously (i.e. later), and will not have been called by
// the time we get here.
}
中的解析可能比解析一个简单的downloadDetails
更复杂,所以只需将闭包的第一个参数更改为适合您的任何数据。案件。