任何人都可以解释弱引用的用法吗?
documentation并没有准确地解释它,它只是说GC可以随时通过弱引用破坏链接到的对象。那么一个物体可以随时消失的重点是什么?如果我需要在它消失后立即使用该怎么办?
你能用一些好的例子解释一下吗?
由于
答案 0 :(得分:34)
事件是弱引用的常见场景。
考虑一对物体:发射器和接收器。接收器的寿命比发射器短。
您可以尝试这样的实现:
class Emitter(object):
def __init__(self):
self.listeners = set()
def emit(self):
for listener in self.listeners:
# Notify
listener('hello')
class Receiver(object):
def __init__(self, emitter):
emitter.listeners.add(self.callback)
def callback(self, msg):
print 'Message received:', msg
e = Emitter()
l = Receiver(e)
e.emit() # Message received: hello
但是,在这种情况下,Emitter会保留对绑定方法callback
的引用,该方法保留对Receiver的引用。所以发射器让接收器保持活力:
# ...continued...
del l
e.emit() # Message received: hello
这有时很麻烦。想象一下,Emitter
是某些数据模型的一部分,它指示数据何时发生变化,而Receiver
是由一个对话框窗口创建的,该对话框窗口会监听该更改以更新某些UI控件。
通过应用程序的生命周期,可以生成多个对话框,并且我们不希望它们的接收器在窗口关闭后很长时间内仍然在Emitter中注册。这将是一次内存泄漏。
手动删除回调是一种选择(同样麻烦),使用弱引用是另一种选择。
有一个很好的类WeakSet
看起来像普通集,但是使用弱引用存储它的成员,并且在它们被释放时不再存储它们。
出色!让我们用它:
def __init__(self):
self.listeners = weakref.WeakSet()
然后再跑:
e = Emitter()
l = Receiver(e)
e.emit()
del l
e.emit()
哦,根本没有任何事情发生!那是因为绑定方法(特定接收者的callback
)现在是孤立的 - 发射器和接收器都没有强烈的引用。因此,它立即收集垃圾。
让我们让接收者(这次不是发射者)对这个回调保持强烈的引用:
class Receiver(object):
def __init__(self, emitter):
# Create the bound method object
cb = self.callback
# Register it
emitter.listeners.add(cb)
# But also create an own strong reference to keep it alive
self._callbacks = set([cb])
现在我们可以观察到预期的行为:只要接收器存在,发射器就只保留回调。
e = Emitter()
l = Receiver(e)
assert len(e.listeners) == 1
del l
import gc; gc.collect()
assert len(e.listeners) == 0
请注意,我必须在此放置gc.collect()
以确保接收器立即被清理干净。这里需要它,因为现在有一个强引用循环:绑定方法指向接收器,反之亦然。
这不是很糟糕;这只意味着接收器的清理将推迟到下一个垃圾收集器运行。简单的引用计数机制无法清除循环引用。
如果你真的想要,你可以通过用自定义函数对象替换绑定方法来删除强引用循环,自定义函数对象也将其self
作为弱引用。
def __init__(self, emitter):
# Create the bound method object
weakself = weakref.ref(self)
def cb(msg):
self = weakself()
self.callback(msg)
# Register it
emitter.listeners.add(cb)
# But also create an own strong reference to keep it alive
self._callbacks = set([cb])
让我们把这个逻辑放到辅助函数中:
def weak_bind(instancemethod):
weakref_self = weakref.ref(instancemethod.im_self)
func = instancemethod.im_func
def callback(*args, **kwargs):
self = weakref_self()
bound = func.__get__(self)
return bound(*args, **kwargs)
return callback
class Receiver(object):
def __init__(self, emitter):
cb = weak_bind(self.callback)
# Register it
emitter.listeners.add(cb)
# But also create an own strong reference to keep it alive
self._callbacks = set([cb])
现在没有强引用循环,所以当释放Receiver
时,回调函数也会立即被释放(并从发射器的WeakSet
中删除),而不需要完整的GC循环
答案 1 :(得分:25)
弱引用的典型用法是如果A有对B的引用而B有对A的引用。没有适当的循环检测垃圾收集器,即使没有引用,这两个对象也永远不会得到GC。要么来自“外部”。但是,如果其中一个引用是“弱”,则对象将正确地进行GC。
然而,Python 确实有一个循环检测垃圾收集器(自2.0以来!),因此不计算:)
弱引用的另一个用途是缓存。它在weakref
文档中提到:
弱引用的主要用途是实现容纳大对象的缓存或映射,其中希望大对象不能仅仅因为它出现在缓存或映射中而保持活动。
如果GC决定销毁其中一个对象,并且您需要它,则可以重新计算/重新获取数据。
答案 2 :(得分:2)
在Observer设计模式中,通常Observable Object必须维护 对Observer对象的弱引用。
例如。 A发出一个事件done()和B向A注册,它想要 听完事件()。因此,每当发出done()时,B就是 收到通知。但如果在申请中不需要B,那么A一定不能 成为A中垃圾收集的一个障碍(因为A持有 参考B)。因此,如果A对B具有弱引用,则何时 所有对A的引用都没有了,那么B将被垃圾收集。