Swift中的@escaping是什么?

由donnywals于2020年3月11日发布
如果您曾经编写或使用过将闭包(闭包的使用:《Swift语言入门实例教程》课程第6章第5节:Swift中的闭包(Closure)详解)作为其参数之一的函数,则很可能遇到了@escaping关键字。 当闭包在Swift中被标记为escaping(转义)时,这意味着该闭包将失效,或者保留传递给它的作用域。 让我们看一个非转义闭包的例子:

func doSomething(using closure: () -> Void) {
  closure()
}

传递给doSomething(using :)的闭包在doSomething(using :)函数中立即执行。 因为闭包是在doSomething(using :)范围内立即执行的,所以我们知道在闭包内部所做的任何事情都不会泄漏或超过doSomething(using :)范围的寿命。 如果我们在doSomething(using :)中进行关闭操作或保留函数作用域,则会收到编译器错误:

func doSomething(using closure: () -> Void) {
  DispatchQueue.main.async {
    closure()
  }
}

上面的代码将导致编译器错误,因为Swift现在可以看到,当我们调用doSomething(using :)时,传递给该函数的闭包将转义其范围。这意味着我们需要将其标记为故意的,因此doSomething(using :)的调用者将知道他们正在处理一个闭包,该闭包将超过传递给它的函数的范围,这意味着他们需要采取措施预防循环引用或内存泄露。除了向调用者告知转义闭包doSomething(using :)之外,它还告诉Swift编译器我们知道闭包保留了传递给它的作用域,对此我们可以接受。

您通常会看到转义闭包以执行异步工作并将函数作为回调调用闭包。例如,URLSession.dataTask(with:completionHandler :)的completionHandler标记为@escaping,因为在请求完成后即完成数据处理程序后即创建数据任务之后的某个时间,由于完成处理程序传递的闭包被执行。如果您编写的代码带有完成处理程序并使用数据任务,则您接受的闭包也必须标记为@escaping。

func makeRequest(_ completion: @escaping (Result<(Data, URLResponse), Error>) -> Void) {
  URLSession.shared.dataTask(with: URL(string: "https://donnywals.com")!) { data, response, error in
    if let error = error {
      completion(.failure(error))
    } else if let data = data, let response = response {
      completion(.success((data, response)))
    }

    assertionFailure("We should either have an error or data + response.")
  }
}

请注意,在上方的结束代码中,标记为@escaping,是因为我在数据任务的完成处理程序中使用了它。 这意味着,在makeRequest(_ :)退出作用域之前,不会执行completion闭包,并且completion闭包的生命周期更长。

简而言之,@escaping用于通知采取闭包的函数的调用者,该闭包可能已存储或超出了接收函数的范围。 这意味着调用方必须采取预防措施,以防止循环引用和内存泄漏。

译自:https://www.donnywals.com/what-is-escaping-in-swift/

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
struct VideoPicker: UIViewControllerRepresentable { @Environment(.presentationMode) private var presentationMode let sourceType: UIImagePickerController.SourceType // let onImagePicked: (UIImage) -> Void let onURLPicked: (URL) -> Void final class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate { @Binding private var presentationMode: PresentationMode private let sourceType: UIImagePickerController.SourceType private let onURLPicked: (URL) -> Void init(presentationMode: Binding<PresentationMode>, sourceType: UIImagePickerController.SourceType, onURLPicked: @escaping (URL) -> Void) { presentationMode = presentationMode self.sourceType = sourceType self.onURLPicked = onURLPicked } func imagePickerController( picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { // let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage // onImagePicked(uiImage) if let url = info[.mediaURL] as? URL{ onURLPicked(url) } presentationMode.dismiss() } func getVideoURL(from assetIdentifier: String, completion: @escaping (URL?) -> Void) { let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [assetIdentifier], options: nil) guard let asset = fetchResult.firstObject else { completion(nil) return } let options = PHVideoRequestOptions() options.version = .original PHImageManager.default().requestAVAsset(forVideo: asset, options: options) { avAsset, , _ in guard let avAsset = avAsset else { completion(nil) return } let url = (avAsset as? AVURLAsset)?.url completion(url) } } func imagePickerControllerDidCancel( picker: UIImagePickerController) { presentationMode.dismiss() } } func makeCoordinator() -> Coordinator { return Coordinator(presentationMode: presentationMode, sourceType: sourceType, onURLPicked: onURLPicked) } func makeUIViewController(context: UIViewControllerRepresentableContext<VideoPicker>) -> UIImagePickerController { let picker = UIImagePickerController() picker.sourceType = sourceType picker.delegate = context.coordinator picker.mediaTypes = ["public.movie"] return picker } func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<VideoPicker>) { } }获取的url内的路径不可用
05-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值