令人困惑的闭包和完成句柄

Posted

技术标签:

【中文标题】令人困惑的闭包和完成句柄【英文标题】:Confusing closures and completion handles 【发布时间】:2016-07-07 01:01:13 【问题描述】:

我是一名新程序员,很迷茫。

我正在学习这个在线 ios 开发课程,并且我正在配置集合视图单元格。 但是,使用了闭包和完成句柄,之前从未提及。

import UIKit

class PersonCell: UICollectionViewCell 

@IBOutlet weak var img: UIImageView!

func configureCell(imgUrl: String) 
    if let url = NSURL(string: imgUrl) 
        downloadImg(url)
            


func downloadImg(url: NSURL) 
    getDataFromURL(url)  (data, response, error) in
        dispatch_async(dispatch_get_main_queue())  () -> Void in
            guard let data = data where error == nil else return
            self.img.image = UIImage(data: data)
        
    


func getDataFromURL(url: NSURL, completion: ((data: NSData?, response: NSURLResponse?, error: NSError?) -> Void)) 

    NSURLSession.sharedSession().dataTaskWithURL(url)  (data, response, error) in
        completion(data: data, response: response, error: error)
     .resume()



有人可以向我解释完成处理程序在“getDataFromURL”函数之后做什么。闭包在做什么? “(数据,响应,错误)”是否被传递? swift 如何知道“数据”应该是“(数据,响应,错误)”中的 NSData 等? “dataTaskWithURL”之后的闭包是做什么的(是设置完成处理程序吗?

谢谢!

【问题讨论】:

阅读文档应该会有所帮助developer.apple.com/library/ios/documentation/Swift/Conceptual/… 【参考方案1】:

这些都是好问题!

closure 只是代码行的集合(也称为块),您可以将其视为变量并像函数一样执行。您可以使用变量名称引用闭包,并且可以像任何其他变量一样在函数调用中将闭包作为参数传递,最终在适当的时候执行代码。闭包可以接受某些参数在其代码中使用,并且可以包含返回值。

例子:

这是一个接受两个字符串作为参数并返回一个字符串的闭包。

let closure: (String, String) -> String =  (a: String, b: String) -> String in
                                              return a + b
                                          

因此,以下将打印“Hello Jack!”:

print(closure("Hello ", "Jack!"))

闭包也有变量类型(就像"hello"String1Int)。变量类型基于闭包接受的参数和闭包返回的值。因此,由于上面的闭包接受两个字符串作为参数并返回一个字符串,它的变量类型是(String, String) -> String。注意:当什么都不返回时(即返回类型是Void),你可以省略返回类型(所以(Int, String) -> Void(Int, String)是一样的)。

completion handler 是一个可以传递给某些函数的闭包。当函数完成时,它会执行闭包(例如,当视图完成动画到屏幕上时,当文件完成下载时等)。

例子:

“完成!”将在视图控制器完成呈现时打印。

let newClosure: () -> Void =  () -> Void in
    print("Done!")

let someViewController = UIViewController(nibName: nil, bundle: nil)
self.presentViewController(someViewController, animated: true, completion: newClosure)

让我们专注于您首先编写的getDataFromURL 函数。它有两个参数:一个NSData 类型的变量和一个(NSData?, NSURLResponse?, NSError?) -> Void 类型的闭包。因此,闭包(命名为completion)采用NSData?NSURLResponse?NSError? 类型的三个参数,并且不返回任何内容,因为这是您在函数声明中定义闭包的方式。

然后您拨打getDataFromURL。如果你阅读documentation,你会看到你作为第二个参数传递给这个函数的闭包在加载任务完成时被执行。 dataTaskWithURL 的函数声明定义了闭包接受和返回的变量类型。在此闭包中,您将调用传递给 getDataFromURL 函数的闭包。

在后一个闭包中(当您调用 getDataFromURL 时,您在 downloadImg 中定义的那个),您正在检查您下载的数据是否不为零,如果不是,则您正在设置数据作为UIImageView 中的图像。 dispatch_async(dispatch_get_main_queue(), ...) 调用只是确保您按照 Apple 的规范在主线程上设置新图像(您可以在其他地方阅读有关线程的更多信息)。

【讨论】:

【参考方案2】:

制作一个 typealias 来理解这很容易:

typealias Handle = (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void
//the func should be 
func getDataFromURL(url: NSURL, completion: Handle)
//when you call it. it needs an url and an Handle
getDataFromURL(url:NSURL, completion: Handle)
// so we pass the url and handle to it 
getDataFromURL(url)  (data, response, error) in
        dispatch_async(dispatch_get_main_queue())  () -> Void in
            guard let data = data where error == nil else return
            self.img.image = UIImage(data: data)
        
    
//setp into the func 
func getDataFromURL(url: NSURL, completion: Handle)
    // call async net work by pass url
    NSURLSession.sharedSession().dataTaskWithURL(url)  (data, response, error) in
        // now data / response / error  we have and we invoke the handle 
        completion(data: data, response: response, error: error)
         .resume()


hope it be helpful :D

【讨论】:

以上是关于令人困惑的闭包和完成句柄的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 闭包和 JavaScript 的区别? [关闭]

javascript 闭包和原型

深入理解JavaScript——闭包

JavaScript闭包

译理解JavaScript闭包——新手指南

Swift 完成处理程序 - 转义尾随闭包