异步调用的空闭包
Posted
技术标签:
【中文标题】异步调用的空闭包【英文标题】:Empty closure for asynchronous calls 【发布时间】:2016-08-29 08:22:29 【问题描述】:最近我一直在做一些事情,一旦函数从 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 函数的。
【问题讨论】:
() -> ()
不是一个空的闭包。它是一个没有参数也没有返回值的闭包的type。
...并在主线程上更新 UI。
【参考方案1】:
你说:
我无法理解
() -> ()
正在做什么或(String?, NSError?) -> ()
正在做什么。
这些构造本身并没有做任何事情。它只是定义了一个closure,一段可以从一个方法传递到另一个方法的代码。在这种情况下,想法是viewDidLoad
可以说“启动一些异步网络请求,继续并立即返回,这样应用程序就不会阻塞主线程(即不会冻结用户界面),但这是一个片段完成后可以调用的代码,以便我可以在异步请求完成后更新我的 UI。”
所以,() -> ()
是说这个变量将持有一个闭包。在() -> ()
中,viewDidLoad
提供的闭包被定义为不带参数且不返回值的闭包。在(String?, NSError?) -> ()
中,表示闭包将传递两个参数,可选字符串和错误引用,但不返回任何值。简而言之,它为下载方法提供了一个机会,如果请求成功则传回字符串值,或者如果请求失败,则返回错误对象。所以viewDidLoad
提供了闭包,下载方法负责在异步请求完成时调用闭包。
你问:
我想知道,为什么像这样创建一个空闭包允许程序首先下载数据,一旦下载数据,然后更新 UI。一切正常吗?
这完全是异步方法中涉及的时间问题。 Alamofire 的 responseJSON
方法立即返回,但它的尾随闭包被异步调用(即稍后,在请求完成后)。因此,如果您想在视图控制器中触发 UI 更新,您可以在 DLDetails
方法中采用自己的完成处理程序模式,并且仅在调用 responseJSON
完成处理程序时才调用其完成处理程序。
顺便说一句,在您的 Alamofire 示例中,请确保将 completed()
inside 放在 responseJSON
闭包中,而不是像您的代码 sn-p 中显示的那样放在它之后。这个想法是在请求完成时调用你的闭包,如果你不把它放在 responseJSON
闭包中,它将在请求完成之前过早地被调用。
您可能会考虑不直接在 DLDetails
内更新模型,而是定义 completed
以传回检索到的数据。例如,如果返回一个字符串,DLComplete
将是(String?) -> ()
(例如,如果请求成功,则传递String
,否则返回nil
)。你也可以传回ErrorType
或NSError
引用,所以如果有错误,视图控制器有机会呈现适合特定类型错误的 UI(例如,身份验证错误可能会触发重新-身份验证流程,而网络连接错误可能会触发不同的 UI)。
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
中的解析可能比解析简单的 String
更复杂,因此只需将闭包的第一个参数更改为适合您情况的任何数据。
【讨论】:
谢谢你,但是,我无法理解“()->()”在做什么或“(String?,NSError?)->()”在做什么......对 ios 开发人员来说还是个新手.. 消化了几个小时的资料,我想我现在明白了!谢谢!以上是关于异步调用的空闭包的主要内容,如果未能解决你的问题,请参考以下文章
记录一些遇见的bug——记录一个使用多线程异步调用openfeign时子线程丢失request请求头导致的空指针异常错误