Подсчет ссылок 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.