Lazy initialisation and retain cycle 延迟初始化和内存循环引用
Lazy initialisation and retain cycle 延迟初始化和内存循环引用
在使用懒加载初始化器时,是否有可能出现保留循环的情况?
在一篇博文和许多其他地方中都可以看到[unowned self]
class Person {
var name: String
lazy var personalizedGreeting: String = {
[unowned self] in
return "你好,\(self.name)!"
}()
init(name: String) {
self.name = name
}
}
我尝试了这样做
class Person {
var name: String
lazy var personalizedGreeting: String = {
//[unowned self] in
return "你好,\(self.name)!"
}()
init(name: String) {
print("person init")
self.name = name
}
deinit {
print("person deinit")
}
}
像这样使用它
//...
let person = Person(name: "姓名")
print(person.personalizedGreeting)
//..
结果发现打印出了"person deinit"。
所以看起来没有保留循环。
根据我的了解,当一个闭包捕获self,并且该闭包被self强引用时,就会出现保留循环。这种情况看起来类似于保留循环,但实际上并不是。
懒初始化和保持循环引用是一个常见的问题,当在闭包中使用self时,可能会导致保持循环引用,从而导致内存泄漏。下面是这个问题出现的原因以及解决方法的总结。
原因:
当在闭包中使用self时,闭包会对self进行强引用,而self又对闭包进行强引用,这样就形成了保持循环引用。
解决方法:
1. 使用weak self或unowned self来避免保持循环引用。在闭包中使用weak self时,需要注意使用可选绑定来解包可选类型。
2. 使用捕获列表来声明闭包中对self的引用方式。捕获列表会在闭包的定义中声明对self的引用方式,可以通过捕获列表中声明的引用方式来避免保持循环引用。
以下是一个示例代码,演示了如何解决懒初始化和保持循环引用的问题:
lazy var personalizedGreeting: String = { [weak self] in
guard let self = self else { return "" }
return self.name
}()
在上述代码中,使用了捕获列表来声明对self的引用方式,将self声明为weak self,然后在闭包中使用可选绑定来解包可选类型。这样就避免了保持循环引用的问题。
懒初始化和保持循环引用是一个常见的问题,可以通过使用weak self或unowned self来避免保持循环引用。另外,也可以使用捕获列表来声明对self的引用方式。通过这些解决方法,可以避免内存泄漏和其他与保持循环引用相关的问题。
懒加载(Lazy initialisation)和循环引用(retain cycle)是iOS开发中常见的问题。懒加载是一种延迟初始化的方式,它可以在需要的时候才创建对象,而不是在对象被实例化时就立即创建。循环引用是指两个或多个对象之间相互持有对方的强引用,导致它们无法被释放,从而造成内存泄漏。
在上述代码示例中,我们可以看到两个问题的出现原因和解决方法。首先,懒加载的闭包在执行时会捕获self(当前对象)作为强引用,这导致了循环引用的产生。其次,如果闭包中的self被捕获为强引用,那么在对象释放之前,闭包是不会被释放的。
解决循环引用的方法是使用捕获列表(capture list),将self捕获为弱引用(weak reference)或无主引用(unowned reference)。这样在闭包中引用self时,不会造成循环引用。在上述代码示例中,如果将懒加载的闭包中的self捕获为无主引用(unowned),就可以避免循环引用的问题。
另外,对于懒加载的闭包,默认情况下并不会自动将self捕获为弱引用或无主引用。如果想要避免循环引用,需要手动将self捕获为弱引用或无主引用。
总结起来,懒加载和循环引用是iOS开发中常见的问题。懒加载可以延迟初始化对象,但如果在懒加载的闭包中捕获self为强引用,就会导致循环引用的产生。为了解决循环引用的问题,可以使用捕获列表将self捕获为弱引用或无主引用。
懒加载和循环引用问题的原因在于懒加载属性只保留了block的返回值,而没有保留block本身。因此,当懒加载属性未初始化时,外部上下文会保留懒加载的block,使其保持一致性,从而避免了循环引用的发生。然而,当懒加载属性被初始化后,懒加载的block会被执行并很快释放,这样也不会发生循环引用。
然而,问题的关键在于谁保留了懒加载的block。我认为这可能不是self,因为如果在block内部捕获self时没有发生循环引用,那么self可能不会保留懒加载的block。至于,我不这样认为。并不意味着没有捕获,而是意味着临时存在,并且没有对象应该对这个block有持久的引用,或者换句话说,保留这个block。这个block不能被异步使用(参考此主题)。如果是这样的话,懒加载的block如何保持到懒加载属性被初始化的时候呢?
解决这个问题的方法是使用weak self来避免循环引用。通过在block内部使用weak self,可以防止self对block的强引用,从而解决循环引用的问题。下面是一种可能的实现方式:
lazy var someProperty: SomeType = { [weak self] in
// 使用weak self来避免循环引用
guard let strongSelf = self else {
return DefaultValue
}
// 在这里使用strongSelf而不是self
// 进行其他操作
return someValue
}()