Подсчет ссылок Vala и передача параметров
После экспериментов с 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 не вставил инкремент отсчета ссылок (RC) дляfoo при передаче его вbar . Так что, насколько я понимаю, первая строка в bar уменьшит RC foo до 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 ответ:
Это правильное поведение. Учитываются только ссылки
owned
. Параметрыunowned
, Если явно не указано иное. Таким образом,f
вbar
никогда не подсчитывается ссылка, потому что вызывающий отвечает за поддержание количества ссылок. Переменные места хранения (поля классов, переменные стека, глобальные переменные) - это всеowned
.Итак, давайте разберем
main
иbar
отдельно:
main
создает экземплярFoo
, который нужно куда-то поместить. Он помещает его в глобальная переменнаяfoo
, которой она принадлежит. Теперь существует единственная ссылка на объект, созданный с помощьюfoo
. Затем мы вызываемbar
, который принимает параметрfoo
. Мы знаем, чтоfoo
уже ссылается на объект и что передача его в качестве параметра не требует увеличения ссылки, если параметр не равенowned
. Поэтому мы просто передаем указательfoo
вbar
.
bar
принимает параметр типаFoo
под названиемf
, которым не владеет. Он присваивает значение null совершенно не связанному объекту. глобальная переменная, называемаяfoo
, уменьшает количество ссылок на объект, очищая его по мере необходимости. Затем он выполняет присваивание полю вf
.Чтобы иметь эту работу "правильно" компилятор должен был бы 1) понимать, что
foo
иf
- это одно и то же, даже если вы можете вызватьbar
с любым параметром, 2) знать, что уменьшение количества ссылок наfoo
немного отличается от уменьшения его до нуля в некоторых случаях. Это просто слишком сложно для любого компилятора. способен решить проблему остановки.Чтобы ваш код работал должным образом, у вас есть два варианта:
Короче говоря, ответственность за то, чтобы переменная оставалась живой, когда она передает ее методу в течение всего срока службы этого метода, лежит на вызывающем объекте. Вы можете себе представить, что становится немного сложнее, когда этот метод
Назначьте новый объект как глобальной переменной
foo
, так и переменной стека, которую вы передаете вbar
. Теперь вы гарантировали, что переменная останется живой через вызовbar
.Пусть
bar
вместоFoo f
возьмутowned Foo f
. Это приведет к тому, что вызывающий объект увеличит ссылку наfoo
перед ее передачей иbar
уменьшит ссылка, когда все будет сделано.async
.