Memory Vala文档中的Gtk示例如何不';不会导致内存泄漏吗?
让我们举一个例子,从。有一个名为Memory Vala文档中的Gtk示例如何不';不会导致内存泄漏吗?,memory,memory-leaks,gtk,vala,Memory,Memory Leaks,Gtk,Vala,让我们举一个例子,从。有一个名为SyncSample的类,它派生自Gtk.Window。这个类向自身添加了两个小部件,即Gtk.SpinButton和Gtk.Scale。根据,这将建立从SyncSample到小部件的硬引用: using Gtk; public class SyncSample : Window { private SpinButton spin_box; private Scale slider; public SyncSample () {
SyncSample
的类,它派生自Gtk.Window
。这个类向自身添加了两个小部件,即Gtk.SpinButton
和Gtk.Scale
。根据,这将建立从SyncSample
到小部件的硬引用:
using Gtk;
public class SyncSample : Window {
private SpinButton spin_box;
private Scale slider;
public SyncSample () {
// ...
spin_box = new SpinButton.with_range (0, 130, 1);
slider = new Scale.with_range (Orientation.HORIZONTAL, 0, 130, 1);
现在是有趣的部分:
spin_box.adjustment.value_changed.connect (() => {
slider.adjustment.value = spin_box.adjustment.value;
});
slider.adjustment.value_changed.connect (() => {
spin_box.adjustment.value = slider.adjustment.value;
});
闭包以一种艰难的方式捕获这个引用,并添加到这些小部件中。因此,有效地,每个小部件都有一个对SyncSample
对象的硬引用。这已经导致硬引用循环:
SyncSample
→ … → <代码>Gtk.旋转按钮
→ … → <代码>同步样本
SyncSample
→ … → <代码>Gtk.刻度→ … → <代码>同步样本this
引用(它们捕获了一个硬this
引用),spin\u框
和滑块
仍然是硬引用,导致我们进入另一个硬引用循环:
→ … → <代码>Gtk.刻度→ … → <代码>Gtk.旋转按钮Gtk.SpinButton
长话短说 我做了一个小实验。我所做的是补充
WeakRef weak_window_ref;
WeakRef weak_slider_ref;
向全球范围和
weak_window_ref = WeakRef( this );
weak_slider_ref = WeakRef( slider );
在SyncSample
的构造函数末尾。
此外,在示例的main
例程中,我添加了
window = null;
stdout.printf( "window leaked? %s\n", weak_window_ref.@get() != null ? "yes" : "no" );
stdout.printf( "slider leaked? %s\n", weak_slider_ref.@get() != null ? "yes" : "no" );
就在Gtk.main()
和返回0
指令之间
运行此设置时,我们看到窗口及其小部件都没有泄漏。这很好,因为这意味着参考周期被打破了——不知何故
现在,让我们对设置做一个小小的修改:删除Gtk.main()
调用,该调用位于window=null
赋值之前。你可能会想,这不应该改变输出,是吗?让我们看看
窗户漏水?对
滑块泄漏?对
因此,这一次的参考周期并没有被打破,并因此而泄漏。这怎么可能?我想,尤其是它是这个问题的答案:
当小部件被销毁时,它在其他对象上保留的所有引用都将被释放:
- 如果小部件位于容器中,它将从其父级中移除
- 如果小部件是一个容器,那么它的所有子部件都将被递归地销毁
SyncSample
类仍然保留了对其小部件的两个硬引用(其类成员)
所以怀疑就来了:窗口及其小部件的交互,由它们的destroy
信号触发,不知何故(请参阅:“它在其他对象上的所有引用都将被释放”)会断开小部件的信号与它们的处理程序的连接。这些处理程序持有对SyncSample
对象的硬引用,因为它们被定义为闭包。因此,尽管从SyncSample
到其小部件的硬引用仍然完好无损,但这两个引用周期被打破:
→ … → <代码>Gtk.旋转按钮→ … → <代码>同步样本SyncSample
→ … → <代码>Gtk.刻度→ … → <代码>同步样本SyncSample
→ … → <代码>Gtk.刻度→ … → <代码>Gtk.旋转按钮Gtk.SpinButton
SyncSample
对象的硬引用,而不捕获对spin_box
和滑块的引用。这些可通过本
参考进行过渡访问:
Gtk.SpinButton
→ <代码>同步样本
→ <代码>Gtk.刻度→ <代码>同步样本→ <代码>Gtk.旋转按钮
因此,前两个周期一旦中断,小部件就会被销毁,这反过来会将SyncSample
的引用计数器重置为零,从而导致其销毁
开放性问题 当触发窗口和小部件的销毁信号时,
SyncSample
的构造函数在其小部件的信号上安装的处理程序如何断开连接
简本 将以下内容视为“带回家的信息”
- 可能,对于子窗口小部件的回调,可以使用闭包和硬引用
,例如,在窗口中,如果该窗口在稍后某个时间显示并特别关闭,或者其this
信号由其他机制发出销毁
- 可能,定制容器小部件应该确保
实现释放对其子小部件的所有引用。这可能已经由destroy
类保证了容器
- 如果您的自定义小部件维护对其他类的硬引用,而这些类又可能具有对该小部件的硬引用,那么这可能是一个错误