考虑以下代码:
class myDropBounceAndRollBehavior: UIDynamicBehavior {
let v = UIView()
init(view v: UIView) {
self.v = v
super.init()
}
override func willMove(to anim: UIDynamicAnimator?) {
guard let anim = anim else {return}
let sup = self.v.superview!
let grav = UIGravityBehavior()
grav.action = { [unowned self] in
let items = anim.items(in: sup.bounds) as! [UIView]
if items.index(of: self.v) == nil {
anim.removeBehavior(self)
self.v.removeFromSuperview()
}
}
self.addChildBehavior(grav)
grav.addItem(self.v)
}
}
这里我们有一个带有函数willMove(anim:)
的类,它具有引用自身的闭包,创建一个保留周期。为了解决这个问题,马特将self
设置为unowned self
以打破这个循环。
他的下一段说:
这里有一个潜在的(而且相当复杂的)保留周期:
self.addChildBehavior(grav)
导致持续引用grav
,grav
持续引用grav.action
,分配给grav.action
的匿名函数指self
。为了打破这个循环,我在匿名函数的捕获列表中将self
的引用声明为unowned
通过本书的摘录,我为下面的情况制定了参考图,
因此,当触发函数willMove(anim:)
时,将创建引用self.addChildBehavior(grav)
的函数调用grav
,从而创建对grav
引用实例的强引用。但由于函数willMove(anim:)
存在于主线程上,函数self.addChildBehavior(grav)
必须在释放willMove(anim:)
的堆内存之前完成,因此self.addChildBehavior(grav)
不再具有对grav
的强引用,并且willMove(anim:)
可以完成并且内存从堆中释放。结果将如下所示:
此时,一旦willMove(anim:)
完成执行,剩下的唯一引用是引用实例的unowned self
和一些引用(例如let behavior = MyDropBounceAndRollBehaviour()
),然后一旦匿名函数执行完毕,那么它只会引用behaviour
的<MyDropAndBounceBehavior>
我有正确的理解吗?
我将尝试用其他话来解释所有权周期:
self.behaviors -> grav -> action -> self
^ created by addChildBehavior(grav)
^ created by grav.action = {
^ created by capturing
self
拥有引力行为。引力行为拥有动作,动作捕获(拥有)self
。
要打破所有权周期,您必须打破其中一个连接。一种解决方案是使用[weak self]
或[unowned self]
打破捕获。
你的推理是错误的。当函数调用(例如willMove
)完成时,不释放堆内存。仅当没有该内存的所有者时,才会释放引用计数内存管理中的堆内存。由于存在所有权周期,因此无法释放内存。线程在这方面确实没有任何作用。如果在同一个线程上调用所有内容(实际上可能发生),则会发生相同的情况。
我认为你的主要错误是self.addChildBehavior(grav)
保留grav
的想法。这不是真的。它永久地将grav
添加到self
持有的行为列表中,这意味着创建一个强大的所有权self -> grav
。
也许这会有所帮助:
Self拥有grav
(作为儿童行为添加)。但grav
有一个拥有self
(行动)的区块。所以现在你拥有拥有self
的self
,这是一个保留周期。你可以在这里使用unowned self
,因为如果self
被释放,grav
的块也将被释放。当unowned self
拥有引用self
的对象时,你通常在这种情况下使用self
;否则,使用weak self
。