当我最近开始学习 Swift 时,我的思维模式是 C#。我发现很难理解,如果一个 Swift 函数想要存储一个闭包类型参数,它必须被标记为 @escaping
。
在 C# 中,如果委托(delegate)将在当前范围之外存储和调用,则此类等价不需要使用任何特殊关键字标记委托(delegate)类型参数。
Swift 中需要 @escaping
关键字的闭包有什么特别之处?关于此语言功能的任何实现细节都会很棒。
最佳答案
在 Swift 中转义闭包并没有什么特别之处。关键字用于告诉 API 的调用者闭包将超出调用 API 的范围。这对 Swift 中的内存管理具有重要意义。
Swift 中没有真正的垃圾收集器。您可能知道您可以拥有强引用或弱引用。强引用是指只要存在,所引用的对象就不能被释放。弱引用是指即使对象仍然存在也可以解除分配的引用。
例如:
class A
{
var foo: B?
weak var bar: B?
}
class B
{
var foo: A?
weak var bar: A?
}
var a = A()
do
{
var b1 = B()
var b2 = B()
a.foo = b1
a.bar = b2
}
在上面,a
持有对b1
的强引用和对b2
的弱引用。当 do
的范围退出时,b2
将被释放,但 b1
将继续存在,因为 a
有一个强烈引用它。
如果我这样做:
b1.foo = a
现在 a
对 b1
具有强引用,而 b1
对 a
具有强引用。这些对象在超出范围时不会被释放,因为每个对象都有对另一个的强引用。它被称为引用循环,它是 Swift 中内存泄漏的主要来源。他们解决这个问题的方法是将一个对象指定为另一个对象的“所有者”,并使从拥有者到所有者的引用成为弱引用。
闭包是 Swift 中的一个对象,它的属性是要执行的代码块和对它捕获的变量的引用。默认情况下,这些是强引用。所以这个闭包:
{ () -> () in print(a) }
捕获 a
作为强引用。这很好,但有一个问题。很容易不小心创建引用循环。请考虑以下事项:
class C
{
private var frobnicator: () -> () = { }
func doFrobnication()
{
frobnicator()
}
var aString = "a value"
func setFrobnicator(_ closure: @escaping () -> ())
{
frobnicator = closure
}
func temporarilyFrobnicate(_ closure: () -> ())
{
closure()
}
}
let c = C()
c.setFrobnicator{ print(c.aString) }
c.temporarilyFrobnicate{ print(c.aString) }
在上述两个调用中,c
都被带有字符串引用的闭包捕获。这对第二种情况很好。闭包对 c
有强引用,但没有对闭包的强引用。闭包不会脱离 temporarilyFrobnicate
的范围。但是,在第一种情况下,c
对闭包有强引用,而闭包对 c
有强引用。我们有一个引用周期。对于调用像 setFrobnicator
这样的函数的程序员来说,知道闭包脱离了函数的范围很重要,这样他们就可以使捕获引用变弱,例如
c.setFrobnicator{ [weak c] in print(c.aString ?? "") }
这个问题对 self
尤其有害,例如如果你试试这个:
extension C
{
func doSomething()
{
setFrobnicator{ print(aString) }
temporarilyFrobnicate{print(aString) }
}
}
看起来您并没有创建任何引用循环,但通过使用 aString
,您实际上捕获了 self
。这就是为什么编译器会在 setFrobnicator{ print(aString) }
上标记一个错误,让您显式地编写 self
以便很明显您已经捕获了它并且您应该确保它被弱捕获。
extension C
{
func doSomething()
{
setFrobnicator{ [weak self] in print(self.aString ?? "") }
temporarilyFrobnicate{print(aString) }
}
}
编译器不会在第二次调用时出错,因为它知道闭包没有转义,就像您所做的那样。
关于c# - Swift 中的闭包转义 VS C# 中的委托(delegate)类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58969918/