引用计数
引用计数类型
引用计数是一种垃圾回收机制,其基于向数据类型或任何内存区域分配计数器;当获取该数据类型的新引用时,计数器会增加;当释放引用时,计数器会减小。一旦释放了最后引用,与该数据类型关联的资源即可释放。
GLib 在许多数据类型中使用引用计数,并提供了 `grefcount` 和 `gatomicrefcount` 类型,以便在新数据类型中实现安全和原子的引用计数语义。
GLib 中的 `grefcount` 和 `gatomicrefcount` 会被当做完全不透明的类型,因此你需要始终使用提供的API 来增加和减少计数器,并且绝不直接检查其内容,也不将其内容与其他值进行比较。
引用计数数据
“引用计数盒”或“RcBox”是一个不透明包装数据类型,可以保证它和给定数据类型大小一致,并且扩展了给定数据类型的引用计数语义以进行内存管理。
假设你有像放在栈中的结构之类的普通旧数据类型,并且希望提供额外的API在堆中使用它;或者如果你想实现一种新类型,以便通过引用传递,而不必实现自己的引用计数或复制/释放语义,则RcBox 很管用。
典型用法是
typedef struct {
char *name;
char *address;
char *city;
char *state;
int age;
} Person;
Person *
person_new (void)
{
return g_rc_box_new0 (Person);
}
每当你希望获取内存上的引用时,都应该调用 `g_rc_box_acquire()`;类似地,当你希望释放引用时,你应该调用 `g_rc_box_release()`
// Add a Person to the Database; the Database acquires ownership
// of the Person instance
void
add_person_to_database (Database *db, Person *p)
{
db->persons = g_list_prepend (db->persons, g_rc_box_acquire (p));
}
// Removes a Person from the Database; the reference acquired by
// add_person_to_database() is released here
void
remove_person_from_database (Database *db, Person *p)
{
db->persons = g_list_remove (db->persons, p);
g_rc_box_release (p);
}
如果你在结构内部分配了额外的内存,则可以使用 `g_rc_box_release_full()`,它提供了一个函数指针,如果释放的引用是最后一个引用,则将调用该函数指针。
void
person_clear (Person *p)
{
g_free (p->name);
g_free (p->address);
g_free (p->city);
g_free (p->state);
}
void
remove_person_from_database (Database *db, Person *p)
{
db->persons = g_list_remove (db->persons, p);
g_rc_box_release_full (p, (GDestroyNotify) person_clear);
}
如果你想要转移引用计数数据类型的所有权而不增加引用计数,则可以使用 `g_steal_pointer()`
Person *p = g_rc_box_new (Person);
// fill_person_details() is defined elsewhere
fill_person_details (p);
// add_person_to_database_no_ref() is defined elsewhere; it adds
// a Person to the Database without taking a reference
add_person_to_database_no_ref (db, g_steal_pointer (&p));
线程安全
对使用 `g_rc_box_alloc()`, `g_rc_box_new()`, 和 `g_rc_box_dup()` 分配的数据的引用计数操作不是线程安全的;你的代码应负责确保在同一线程上获取和释放引用。
如果你需要线程安全的引用计数,你应该使用 `g_atomic_rc_*` API
操作 | 原子等价 |
---|---|
g_rc_box_alloc() |
g_atomic_rc_box_alloc() |
g_rc_box_new() |
g_atomic_rc_box_new() |
g_rc_box_dup() |
g_atomic_rc_box_dup() |
g_rc_box_acquire() |
g_atomic_rc_box_acquire() |
g_rc_box_release() |
g_atomic_rc_box_release() |
g_rc_box_release_full() |
g_atomic_rc_box_release_full() |
对使用 `g_atomic_rc_box_alloc()`, `g_atomic_rc_box_new()`, 和 `g_atomic_rc_box_dup()` 分配的数据的引用计数操作保证为原子操作,因此不同线程可以安全地执行它。必须注意,只有引用获取和释放是原子的;数据的内客容更改由你负责。
混合原子和非原子引用计数操作是一个编程错误。
自动指针清理
如果您想通过引用计数向普通旧数据类型添加 g_autoptr()
支持,则可以使用 G_DEFINE_AUTOPTR_CLEANUP_FUNC()
和 g_rc_box_release()
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MyDataStruct, g_rc_box_release)
如果您需要清除数据的内容,您需要使用一个辅助函数来调用 g_rc_box_release_full()
static void
my_data_struct_release (MyDataStruct *data)
{
// my_data_struct_clear() is defined elsewhere
g_rc_box_release_full (data, (GDestroyNotify) my_data_struct_clear);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MyDataStruct, my_data_struct_release)
g_rc_box*
和 g_atomic_rc_box*
API 在 GLib 2.58 中引入。