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

Confusing closures and completion handles

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

我正在参加这个在线 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 如何知道 "data" 应该是“(数据,响应,错误)”中的 NSData 等? "dataTaskWithURL" 之后的闭包是做什么的(它是在设置完成处理程序吗?

谢谢!

做一个类型别名来理解这很容易:

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

这些都是好问题!

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

示例:

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

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

因此,下面将打印 "Hello Jack!":

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

闭包也有一个变量类型(就像 "hello" 是一个 String1 是一个 Int)。变量类型基于闭包接受的参数和闭包 returns 的值。因此,由于上面的闭包接受两个字符串作为参数并且 returns 是一个字符串,因此它的变量类型是 (String, String) -> String。注意:当没有 returned 时(即 return 类型是 Void),你可以省略 return 类型(所以 (Int, String) -> Void(Int, String)).

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

示例:

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

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? 的参数,而 return 什么都没有,因为这是您如何在函数声明中定义闭包。

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

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