Как правильно обрабатывать плавающие ссылочные параметры в GLib?

#glib #gobject

#glib #gobject

Вопрос:

В GLib есть плавающие ссылочные объекты.

 container = create_container();
container_add_child(container, create_child());
 

В этом примере create_child функция создает плавающий объект, который никому не принадлежит. container_add_child Функция становится владельцем объекта и становится единственным владельцем объекта. Когда он отключает объект, объект освобождается.

Но что, если container_add_child функция не стала владельцем объекта? В этом случае мы получим утечку памяти. Да, объект никому не принадлежит, но он все еще существует в памяти, и никто не освобождает память объекта.

Что, если мы напишем функцию, которая принимает такой объект в качестве параметра.

 void handle_object(gpointer object)
{
    g_object_ref_sink(object);
    
    // Doing something with object
    
    g_object_unref(object);
}
 

Должны ли мы добавлять g_object_ref_sink / g_object_unref к каждой функции, которая принимает потенциально плавающий ссылочный объект в качестве параметра? Если мы удалим эти строки, то кто-то может сделать такой вызов: handle_object(create_child()) и мы получим утечку памяти. Объект будет «плавающим» в памяти навсегда.

Ответ №1:

Должны ли мы добавлять g_object_ref_sink / g_object_unref к каждой функции, которая принимает потенциально плавающий ссылочный объект в качестве параметра?

Да, это одна из причин, по которой создание API вокруг плавающих ссылок — это боль. Лучше не использовать их и вместо этого использовать явные аннотации передачи прав собственности ( (transfer none) или (transfer full) ).

Ответ №2:

Нужно ли добавлять g_object_ref_sink / g_object_unref к каждой функции, которая принимает потенциально плавающий ссылочный объект в качестве параметра? Если мы удалим эти строки, то кто-то может выполнить такой вызов: handle_object(create_child()) и мы получим утечку памяти. Объект будет «плавающим» в памяти навсегда.

Вопрос мудреца содержит половину ответа. Хорошей идеей будет добавить ref / unref к любому объекту, который вы используете. Это может быть не так в однопоточном приложении или методе GUI, где вы можете быть на 100% уверены, что никто даже не имеет возможности уничтожить объект. Но в многопоточных приложениях объект может быть отменен в любой момент, и это приведет к аннулированию этого объекта.

Однако необходимость двух отдельных методов ( ref_sink и ref ) мне не ясна после 5 лет работы с GTK 🙂

Ответ №3:

Пытаюсь ответить на мой вопрос.

Нужно ли добавлять g_object_ref_sink / g_object_unref к каждой функции, которая принимает потенциально плавающий ссылочный объект в качестве параметра?

Я бы сказал, не передавайте «потенциально» плавающие ссылки функциям, которые их не ожидают. Плавающая ссылка является особенной. Вы не можете получить доступ к объекту, на который ссылается плавающая ссылка, потому что он вам не принадлежит (он никому не принадлежит). Перед использованием таких объектов вы должны вызвать g_object_ref_sink , чтобы получить право собственности. Или передайте ссылку на функцию, которая становится владельцем (но после вызова такой функции вы все равно не сможете использовать объект, потому что он вам все еще не принадлежит).

Итак, если функция предполагает, что вы являетесь владельцем объекта, не передавайте на него плавающую ссылку. В этом случае вам не нужно добавлять g_object_ref_sink / g_object_unref к таким функциям.

Если по каким-то причинам функция принимает «потенциально» плавающую ссылку, то да, вам нужно добавить g_object_ref_sink / g_object_unref к ней. Но если функция не сохраняет ссылку где-нибудь, чтобы использовать ее позже, то эта функция, вероятно, плохо спроектирована. Если функция использует объект только локально (только во время вызова), то не добавляйте g_object_ref_sink g_object_unref к нему / и не передавайте на него плавающие ссылки. Если функция сохраняет ссылку для последующего использования, ее g_object_ref_sink необходимо добавить к ней, чтобы получить право собственности. Но в этом случае в g_object_unref этой функции не будет вызова, потому что кто-то собирается использовать ссылку позже, после завершения работы функции.

Итак, я бы сказал, добавление пары g_object_ref_sink и g_object_unref в функцию не в порядке. Добавление только g_object_ref_sink для получения права собственности на использование ссылки позже после завершения работы функции — это нормально.

Да, это усложняет задачу, потому что вы всегда должны заботиться о том, является ли ссылка плавающей или нет. Вот почему мне лично не нравится идея с плавающей ссылкой. Но это также упрощает работу в таких случаях, как добавление новых объектов в контейнеры.