C# Swift中的转义闭包与C中的委托类型#
当我最近开始学习Swift时,我的心态是C#型的。我发现很难理解,如果Swift函数想要存储闭包类型参数,它必须标记为C# Swift中的转义闭包与C中的委托类型#,c#,swift,closures,C#,Swift,Closures,当我最近开始学习Swift时,我的心态是C#型的。我发现很难理解,如果Swift函数想要存储闭包类型参数,它必须标记为@escaping 在C#中,如果委托稍后将在当前范围之外存储和调用,则这种等价性不需要使用任何特殊关键字标记委托类型参数 Swift中需要@escaping关键字的闭包有什么特殊之处?关于此语言特性的任何实现细节都将非常有用 Swift中的转义闭包没有什么特别之处。关键字用于告诉API调用方闭包将超出调用API的范围。这对Swift中的内存管理具有重要意义 Swift中没有真正
@escaping
在C#中,如果委托稍后将在当前范围之外存储和调用,则这种等价性不需要使用任何特殊关键字标记委托类型参数
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
,但没有任何内容强烈引用闭包。闭包不属于临时frobnite
的范围。但是,在第一种情况下,c
强烈引用了闭包,闭包强烈引用了c
。我们有一个参考周期。对于调用函数(如setFrobInstactor
)的程序员来说,重要的是要知道闭包超出了函数的作用域,因此他们可以使捕获引用变弱,例如
c.setFrobnicator{ [weak c] in print(c.aString ?? "") }
该问题对self
尤其有害,例如,如果您尝试以下操作:
extension C
{
func doSomething()
{
setFrobnicator{ print(aString) }
temporarilyFrobnicate{print(aString) }
}
}
看起来您没有创建任何引用循环,但是通过使用aString
您实际上捕获了self
。这就是为什么编译器会在setfrobInstactor{print(aString)}
上标记错误,以使您显式地编写self
,这样您就可以明显地捕获到它,并且您应该确保捕获得很弱
extension C
{
func doSomething()
{
setFrobnicator{ [weak self] in print(self.aString ?? "") }
temporarilyFrobnicate{print(aString) }
}
}
编译器不会在第二次调用时出错,因为它知道闭包没有转义,就像您所做的那样。@当闭包在作为参数的函数完成执行后被调用时,会使用转义。但是我不能说为什么/如何这对Swift是特别的,因为我不知道C。关键是我们在Swift中区分了
@转义
和非转义闭包,因为编译器可以使用非转义闭包执行某些优化。