Vala引用计数和参数传递

时间:2013-01-18 10:10:00

标签: vala reference-counting

在尝试使用Vala并检查生成的C源代码后,我想出了以下Vala代码:

class Foo : GLib.Object {
    public string baz;
}

class Main : GLib.Object {
    public static Foo foo;

    public static void bar(Foo f) {
        foo = null;
        f.baz = "Hi";
    }

    public static int main(string[] args) {
        foo = new Foo();
        bar(foo);
        return 0;
    }
}

检查生成的C代码我意识到Vala编译器在将 foo 传递给 bar 时没有插入引用计数(RC)增量。所以据我所知, bar 中的第一行会将 foo 的RC减少为0,这反过来会释放 foo ,有效地使传递的变量 f 成为悬挂指针,然后在 bar 的第二行访问该指针。但是,程序执行没有问题,所以我不确定我是否在这里遗漏了一些东西,或者它只是纯粹的巧合。 这里生成的C代码供参考:

void main_bar (Foo* f) {
    Foo* _tmp0_;
    gchar* _tmp1_;
    g_return_if_fail (f != NULL);
    _g_object_unref0 (main_foo);
    main_foo = NULL;
    _tmp0_ = f;
    _tmp1_ = g_strdup ("Hi");
    _g_free0 (_tmp0_->baz);
    _tmp0_->baz = _tmp1_;
}

gint main_main (gchar** args, int args_length1) {
    gint result = 0;
    Foo* _tmp0_;
    Foo* _tmp1_;
    _tmp0_ = foo_new ();
    _g_object_unref0 (main_foo);
    main_foo = _tmp0_;
    _tmp1_ = main_foo;
    main_bar (_tmp1_);
    result = 0;
    return result;
}

1 个答案:

答案 0 :(得分:2)

这是正确的行为。仅计算owned个引用。参数为unowned,除非另有明确说明。因此,f中的bar永远不会被引用计数,因为调用者负责维护引用计数。变量存储位置(类字段,堆栈变量,全局变量)都是owned

所以,让我们分别检查mainbar

main创建Foo的实例,需要将其放在某处。它将它放在拥有它的全局变量foo中。现在,通过foo创建的对象有一个引用。然后,我们调用bar,其中包含参数foo。我们知道foo已经引用了该对象,并且将它作为参数传递不需要我们增加引用,除非参数是owned。因此,我们只需将指针foo传递给bar

bar采用名为Foo的{​​{1}}类型的参数,该参数不属于此类。它为一个名为f的完全不相关的全局变量赋值null,这会减少对象的引用计数,并在必要时对其进行清理。然后,它会对foo中的字段进行分配。

要使“正确”工作,编译器必须1)理解ffoo是相同的,即使您可以使用任何参数调用f,2)知道在bar上递减引用计数与在某些情况下将其递减到零略有不同。对于任何无法解决暂停问题的编译器来说,这太复杂了。

要使代码按预期工作,您有两种选择:

  1. 将新对象分配给全局变量foo和传递给foo的堆栈变量。您现在已通过调用bar确保变量保持有效。

  2. bar取代bar而不是owned Foo f。这将导致调用者在传递Foo f之前递增引用,并且foo在引用完成时递减引用。

  3. 简而言之,调用者有责任确保变量在传递给该方法的生命周期的方法时仍然存在。您可以想象,当该方法为bar时会变得更复杂。