编程iOS开发者

在Swift中,escaping和non-escaping闭包是什么?如何正确组织闭包的工作,使用中存在哪些细微差别,以及在处理异步代码时可能带来什么风险?

用 Hintsage AI 助手通过面试

答复。

Escaping闭包是指"逃离"调用函数作用域的闭包(例如,为异步执行保存)。

在Swift中,函数默认接受non-escaping闭包:闭包在函数调用范围内执行。

为了明确指定escaping使用关键字@escaping

func asyncTask(completion: @escaping () -> Void) { DispatchQueue.main.async { completion() } }

关键区别:

  • Escaping闭包可以被存储并在稍后调用。
  • 在escaping闭包中捕获self时,必须明确指示[weak self][unowned self]以防止内存泄漏(retain cycle)。

陷阱问题。

在传递给调用DispatchQueue.async的函数参数的闭包时,是否总是需要写@escaping?

— 是的。因为DispatchQueue.async会在执行前保存闭包,闭包将变为escaping。

示例:

func foo(action: () -> Void) { DispatchQueue.main.async { action() // 编译不通过:闭包必须是escaping。 } } // 应该: func bar(action: @escaping () -> Void) { ... }

由于不知道该主题的细微差别而产生的实际错误示例。


故事

控制器在escaping闭包内对self创建了强引用(例如网络请求)。控制器在离开屏幕后未被释放——强retain cycle。解决方案:使用[weak self][unowned self]


故事

函数将闭包传递给DispatchQueue.async,但未将其标记为escaping。项目未能编译,错误由于函数嵌套而难以找到。


故事

在闭包内访问了一个在闭包调用时已经被析构的对象(使用了[unowned self])。结果——运行时崩溃。解决方案:使用[weak self]并进行nil检查。