本文将尝试描述捕获时的周期参考和闭合的行为。一旦我们理解了这些事情,我们将继续如何打破导致内存泄漏的周期参考。
本文将使用简单的语言来描述事物,并且不会引入复杂的单词。目的是了解正在发生的事情,而不是对深度的技术复杂性和术语进行漂移。
我们不会在此处使用逃避关闭。您可以推断出它在逃脱关闭方面的工作方式。确定使用弱舞蹈的需求取决于您的上下文,并且本文不会涵盖。如果您有兴趣,可以阅读You don’t (always) need [weak self] | by Besher Al Maleh | Medium。
什么是周期参考?
是一个实例A拥有实例B,而实例B拥有实例A。因此,创建周期参考。
为什么周期参考不好?
因为当持有实例的变量强烈变为零时,实例A仍然活着。虽然它仍然活在记忆中,但我们无法访问或销毁它。如果我们不断创建它的实例,它们最终将堆叠在内存中,从而导致内存泄漏。
这是一个示例:
class Person {
var dog: Dog?
func adoptAdog(_ dog: Dog) {
self.dog = dog
dog.owner = self // cycle reference!
}
}
class Dog {
var owner: Person?
}
func simulateAdoptingAdog() {
let person = Person()
let dog = Dog()
person.adoptAdog(dog)
}
simulateAdoptingAdog() // Instances in memory: Person=1, Dog=1
simulateAdoptingAdog() // Instances in memory: Person=2, Dog=2
simulateAdoptingAdog() // Instances in memory: Person=3, Dog=3
您注意到,在 simulateadoptingadog 方法中,我们创建了一个人和狗实例,并使用了引起周期参考的 indemadog 方法。这两个实例互相参考。
我们可以使用弱属性避免这种情况:
class Dog {
weak var owner: Person?
}
现在,一旦分配了狗的实例,就不会强烈持有实例。
在本文中,我们将讨论封闭引入的周期参考。在人和狗课上,我们可以清楚地看到有关如何引入周期参考的流程。但是,封闭是隐式的。通过了解关闭的行为,我们将知道如何避免循环参考。
什么是软弱而强壮的舞蹈?
虚弱而强壮的舞蹈获得了名字,因为开发人员经常使用它。如果您不想考虑封闭中的复杂自行车参考的复杂性,则开发人员只使用它。这是一个实例被封闭薄弱捕获的情况,并且将强烈持有关闭实施的局部变量。有时,您可以在Objective-C代码上看到它们,例如弱和 strontify 宏。在Swift中,我们这样做:
let someClosure = { [weak self] in
guard let self = self else { return }
// code here
}
使用弱舞的具体原因是什么?
至少有两个原因:
-
打破周期参考。
-
保持实例的活力,直到实现封闭完成为止。
我们如何判断关闭的实施是否已经完成?
实施可能结束的原因有很多。我只会在这里提及三个基本原因:
-
最后一行的代码已经完成
-
返回关键字称为
-
投掷关键字称为
let someClosure = { [weak self] in
guard let self = self else {
return // will end the implmentation
}
guard !self.name.isEmpty else {
throw SomeError.nameEmpty // will end the implementation
}
print(self.name)
... // some lines of code here
print(self.name) // after this line is executed, the implementation is considered finished
}
我们如何判断关闭是否引入了周期参考?
是关闭内部使用的所有者,从而捕获所有者的实例并强烈地保持。
class Person {
lazy var goToWorkClosure = {
print("pay the bus")
self.rideTheBus() // cycle reference!
}
func rideTheBus() {
print("ride the bus")
}
}
var person: Person? = Person()
person?.goToWorkClosure() // [1]
person = nil // [2]
-
[1]â执行此代码线后,将创建闭合实例。 GotoWorkcluse的实例属性强烈引用了关闭。因此,封闭实例归人实例所有。而人实例也归封闭。从而创建一个参考周期。
-
[2]人的实例不会被破坏。如果没有被摧毁,封闭也不会被销毁。
为什么关闭会强烈捕捉实例?
因为我们通过调用 self.rideabus 。。。
一个简单的解释是因为默认情况下关闭,请确保其实现内部使用的外部变量实例已经存在。因此,一旦执行,它仍然可以使用这些实例。因此,它强烈捕获了这些实例。
如何修改此默认行为?
我们可以通过明确定义捕获列表来修改此行为。通常,闭合隐式捕获清单,并具有很强的参考。
使用捕获列表,我们可以告诉它易于捕获某个变量:
class Person {
lazy var goToWorkClosure = { [weak self]
print("pay the bus")
self?.rideTheBus()
}
func rideTheBus() {
print("ride the bus")
}
}
var person: Person? = Person()
person?.goToWorkClosure() // [1]
person = nil // [2]
-
[1] - 调用此方法时不会引入参考周期
-
[2] - 人实例将与其保留的实例一起销毁(在这种情况下,它仅容纳闭合实例)
这将打破周期参考,类似于我与人类和狗课引入的第一个示例。
您会注意到我们已经使用了 self ?当调用 ridebuss 方法时。这是因为 self 可以随时无所作为。如果当时自我为零,则ridethebus不会执行。
尽管 self?.ridethebus()代码线将永远不会发生的地点,因为一旦自我被交易或销毁,这种封闭就会立即被销毁。 P>
但是,如果您在另一个线程上分发了闭合,使其异步运行,则该线程将拥有该线程,直到实现完成为止。如果闭合包含[弱自我],它将执行 self?.ridethebus()。我的意思是执行此代码行是在调用 ridethebus 方法之前首先检查是否存在。如果没有,该方法将不会执行。
使用 [弱自我] ,闭合将弱捕获自我。但是,这是安全的,使用自我的实施中的其他代码不能保证它们都会运行。曾经有一段时间,您的代码的前半部分使用自我完美地运行,但是下半场不会。为什么?因为当时自我被摧毁了。
在执行实施时,我们如何确保自我还活着?
我们使用弱强大的舞蹈!闭合捕获自我微弱,而实施的局部变量则坚强地保持自我。
class Person {
lazy var goToWorkClosure = { [weak self]
guard let self = self else { return }
print("pay the bus")
self?.rideTheBus()
}
func rideTheBus() {
print("ride the bus")
}
}
var person: Person? = Person()
person?.goToWorkClosure()
person = nil
请注意, self 请参阅实例。实现是指关闭的 {} 内部的代码。实施完成后,所有本地变量将被破坏。因此,在实例上释放了保留。
结论
了解软弱和自我舞蹈的行为可能是一个挑战。您必须将自动参考计数(ARC)视为先决条件和封闭行为。由于这里没有提及ARC,因此我试图解释初学者会理解的弱舞。
如果您不是初学者,并且已经了解弧线,那么您也可以在这里学到一些东西。您的想法可能会有一些疑问,您现在已经忽略了很多时间。阅读本文可能会有所帮助,或者可能会导致您提出更多问题。
随后,我可能会在接下来的几周内创建另一个,并提供更详细的示例和解释。