3

在尝试了 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;
}
4

1 回答 1

2

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

所以,让我们分别检查main一下bar

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

bar接受一个Foo名为fis 不拥有的类型的参数。它将 null 分配给一个名为 的完全不相关的全局变量foo,这会减少对象的引用计数,并在必要时对其进行清理。然后它对f.

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

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

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

  2. bar取而代之。_ owned Foo f_ Foo f这将导致调用者foo在传递它之前增加引用,并bar在完成时减少引用。

简而言之,调用者有责任确保在将变量传递给方法时该变量在该方法的生命周期内保持有效。你可以想象当那个方法是async.

于 2013-01-18T14:33:15.003 回答