duy nhất hai trong số các bạn thực sự cần phải quan tâm, trừ khi bạn đang cố gắng để làm việc trên một số mã mức rất thấp là GValue và GParamType
Tôi sẽ bắt đầu với GParamType
GParamType
là dành cho được sử dụng để đăng ký tài sản với GObject. Ví dụ, tôi có một lớp con GObject được gọi là Person, và tôi muốn nó có hai thuộc tính: Name và Age. Trong class_init
chức năng tôi sẽ đăng ký các như vậy
{
GParamSpec *pspec;
. . .
pspec = g_param_spec_string ("name", "Name", "The name of the person", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_NAME, pspec);
pspec = g_param_spec_int ("age", "Age", "The age of the person", 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_AGE, spec);
. . .
}
Bây giờ bạn có thể gọi g_object_get
hoặc g_object_set
trên những tài sản và hệ thống sẽ biết làm thế nào để xử lý nó
char *name;
int age;
g_object_set (G_OBJECT (person), "name", "Steve", "age", 37, NULL);
g_object_get (G_OBJECT (person), "name", &name, "age", &age, NULL);
g_print ("%s is %d years old\n", name, age);
// And because the type system knows when a property is a string, it knows how to give
// you a copy of the string, so you need to free it once you've finished with it
g_free (name);
Các thông số khác nhau được giải thích ở đây: GParamSpec Có các loại GValue cho tất cả các loại tiêu chuẩn: chuỗi, bool, ints vv và một số thư viện khác như GStreamer sẽ đăng ký các tùy chỉnh riêng của chúng.
Ngoài các thuộc tính cài đặt trên GObjectClass bạn rất hiếm khi cần phải xử lý GParamSpec. Hai dịp chính mà chúng xuất hiện là trong các phương thức GObjectClass set/get_property và GObject thông báo tín hiệu. Nó rất hữu ích trong trường hợp cuối cùng để phát hiện tài sản đã nhận được tín hiệu thông báo, bằng cách gọi g_param_spec_get_name
, nhưng thực sự nó tốt hơn để sử dụng thông báo cho cụ thể hơn tín hiệu như vậy:
g_signal_connect (person, "notify::name", G_CALLBACK (name_changed_cb), NULL);
g_signal_connect (person, "notify::age", G_CALLBACK (age_changed_cb), NULL);
hơn
g_signal_connect (person, "notify", G_CALLBACK (something_changed_cb), NULL);
Đôi khi bạn có thể muốn tạo cấu trúc của riêng bạn và sử dụng cấu trúc đó cho các thuộc tính. Ví dụ: nếu tôi có
struct _PersonDetails {
char *name;
int age;
}
và thay vì có hai thuộc tính trên đối tượng Person, tôi muốn có một thuộc tính "chi tiết". Hệ thống loại GLib không biết cách xử lý struct _PersonDetails
tùy chỉnh của tôi vì vậy tôi sẽ cần phải tạo một kiểu hộp cho nó, để nó biết cách sao chép/giải phóng một cách chính xác cấu trúc khi nó được truyền xung quanh nội bộ Glib. Và đó là nơi GValue
có sẵn.
là để gói các giá trị khác nhau để chúng có thể được sao chép và giải phóng chính xác (nếu cần) và để có thể sử dụng các chức năng chung.
Ví dụ, GObjectClass phương pháp set_Property có nguyên mẫu của
void set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
này có nghĩa là bất kỳ loại có thể được đại diện bởi một GValue có thể được truyền trong và chức năng cụ thể như set_int_property, set_string_property, set_bool_property không cần thiết.
Điều này cũng có nghĩa là các hàm g_object_set
và g_object_get
biết cách xử lý các tham số được truyền bởi vì nó biết rằng "tên" thuộc tính được đăng ký là loại chuỗi và có chức năng cần thiết để sao chép/miễn phí chuỗi đó.
thêm về GValue thể tìm thấy ở đây - Generic values
Để đăng ký tùy chỉnh của chúng tôi với struct _PersonDetails
những hệ thống kiểu GLib chúng ta sẽ tạo ra một loại tùy chỉnh đóng hộp mà nói hệ thống như thế nào để sao chép và miễn phí nó. Các chi tiết đang ở đây: Boxed Types
G_DEFINE_BOXED_TYPE (PersonDetails, person_details,
person_details_copy,
person_details_free)
. . .
static gpointer
person_details_copy (gpointer data)
{
struct _PersonDetails *details = (struct _PersonDetails *)data;
struct _PersonDetails *copy = g_new (struct _PersonDetails, 1);
// We need to copy the string
copy->name = g_strdup (details->name);
copy->age = details->age;
return (gpointer) copy;
}
static void
person_details_free (gpointer data)
{
struct _PersonDetails *details = (struct _PersonDetails *)data;
// name was allocated so it needs freed as well
g_free (details->name);
g_free (details);
}
Bây giờ chúng ta có thể đăng ký kiểu của chúng tôi sử dụng
pspec = g_param_spec_boxed ("details", "Details", "The person's details", person_details_get_type(), G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_DETAILS, pspec);
lời giải thích tốt nhất và rõ ràng nhất về chủ đề này tôi đọc đến bây giờ. +1 – drahnr
cuộc gọi g_param_spec_gtype() là sai trong trường hợp này: đối với các loại được đóng hộp, bạn nên sử dụng g_param_spec_boxed(); g_param_spec_gtype() dành cho các thuộc tính lưu trữ giá trị GType thực tế. Ngoài ra, để đăng ký một kiểu đóng hộp, bạn nên sử dụng macro G_DEFINE_BOXED_TYPE(), tạo ra hàm get_type() cho bạn. – ebassi
@ebassi đã sửa cuộc gọi _spec_boxed. – iain