mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-12 20:03:28 +01:00
278 lines
12 KiB
Markdown
278 lines
12 KiB
Markdown
# Instance and class
|
|
|
|
This section and the following four sections are explanations about the next version of the text file editor (tfe).
|
|
It is tfe5.
|
|
It has many changes from the prior version.
|
|
All the sources are listed in [Section 15](sec15.src.md).
|
|
They are located in two directories, [src/tfe5](tfe5) and [src/tfetextview](tfetextview).
|
|
|
|
## Encapsulation
|
|
|
|
We've divided C source file into two parts.
|
|
But it is not enough in terms of encapsulation.
|
|
|
|
- `tfe.c` includes everything other than TfeTextView.
|
|
It should be divided at least into two parts, `tfeapplication.c` and `tfenotebook.c`.
|
|
- Header files also need to be organized.
|
|
|
|
However, first of all, I'd like to focus on the object TfeTextView.
|
|
It is a child object of GtkTextView and has a new member `file` in it.
|
|
The important thing is to manage the Gfile object pointed by `file`.
|
|
|
|
- What is necessary to GFile when generating (or initializing) TfeTextView?
|
|
- What is necessary to GFile when destructing TfeTextView?
|
|
- TfeTextView should read/write a file by itself or not?
|
|
- How it communicates with objects outside?
|
|
|
|
You need to know at least class, instance and signals before thinking about them.
|
|
I will explain them in this section and the next section.
|
|
After that I will explain:
|
|
|
|
- Organizing functions.
|
|
- How to use GtkFileChooserDialog
|
|
|
|
## GObject and its children
|
|
|
|
GObject and its children are objects, which have both class and instance.
|
|
First, think about instance of objects.
|
|
Instance is structured memories and described as C language structure.
|
|
The following is a structure of TfeTextView.
|
|
|
|
~~~C
|
|
/* This typedef statement is automatically generated by the macro G_DECLARE_FINAL_TYPE */
|
|
typedef struct _TfeTextView TfeTextView;
|
|
|
|
struct _TfeTextView {
|
|
GtkTextView parent;
|
|
GFile *file;
|
|
};
|
|
~~~
|
|
|
|
The members of the structure are:
|
|
|
|
- `parent` is the instance structure of GtkTextView which is the parent object of TfeTextView.
|
|
- `file` is a pointer to GFile. It can be NULL if no file corresponds to the TfeTextView object.
|
|
|
|
Notice the program above is the declaration of the structure, not the definition.
|
|
So, no memories are allocated at this moment.
|
|
They are to be allocated when `tfe_text_view_new` function is invoked.
|
|
|
|
You can find the declaration of the ancestors of TfeTextView in the source files of GTK and GLib.
|
|
The following is extracts from the source files (not exactly the same).
|
|
|
|
~~~C
|
|
typedef struct _GObject GObject;
|
|
typedef struct _GObject GInitiallyUnowned;
|
|
struct _GObject
|
|
{
|
|
GTypeInstance g_type_instance;
|
|
volatile guint ref_count;
|
|
GData *qdata;
|
|
};
|
|
|
|
typedef struct _GtkWidget GtkWidget;
|
|
struct _GtkWidget
|
|
{
|
|
GInitiallyUnowned parent_instance;
|
|
GtkWidgetPrivate *priv;
|
|
};
|
|
|
|
typedef struct _GtkTextView GtkTextView;
|
|
struct _GtkTextView
|
|
{
|
|
GtkWidget parent_instance;
|
|
GtkTextViewPrivate *priv;
|
|
};
|
|
~~~
|
|
|
|
In each structure, its parent instance is declared at the top of members.
|
|
So, every ancestors is included in the child instance.
|
|
This is very important.
|
|
It guarantees a child widget to derive all the features from ancestors.
|
|
The structure of `TfeTextView` is like the following diagram.
|
|
|
|
![The structure of the instance TfeTextView](../image/TfeTextView.png){width=14.39cm height=2.16cm}
|
|
|
|
|
|
## Generate TfeTextView instance
|
|
|
|
The function `tfe_text_view_new` generates a new TfeTextView instance.
|
|
|
|
@@@include
|
|
tfetextview/tfetextview.c tfe_text_view_new
|
|
@@@
|
|
|
|
When this function is run, the following procedure is gone through.
|
|
|
|
1. Initialize GObject part in TfeTextView instance.
|
|
2. Initialize GtkWidget part in TfeTextView instance.
|
|
3. Initialize GtkTextView part in TfeTextView instance.
|
|
4. Initialize TfeTextView part in TfeTextView instance.
|
|
|
|
Step one through three is done automatically.
|
|
Step four is done by the function `tfe_text_view_init`.
|
|
|
|
> In the same way, `gtk_text_view_init`, `gtk_widget_init` and `g_object_init` is the initialization functions of GtkTextView, GtkWidget and GObject respectively.
|
|
> You can find them in the GTK or GLib source files.
|
|
|
|
@@@include
|
|
tfetextview/tfetextview.c tfe_text_view_init
|
|
@@@
|
|
|
|
This function just initializes `tv->file` to be `NULL`.
|
|
|
|
## Functions and Classes
|
|
|
|
In Gtk, all objects derived from GObject have class and instance.
|
|
Instance is memories which has a structure defined by C structure declaration as I mentioned in the previous two subsections.
|
|
Each object can have more than one instance.
|
|
Those instances have the same structure.
|
|
Instance, which is structured memories, only keeps status of the instance.
|
|
Therefore, it is insufficient to define its behavior.
|
|
We need at least two things.
|
|
One is functions and the other is class.
|
|
|
|
You've already seen many functions.
|
|
For example, `tfe_text_view_new` is a function to generate TfeTextView instance.
|
|
These functions are similar to object methods in object oriented languages such as Java or Ruby.
|
|
Functions are public, which means that they are expected to be used by other objects.
|
|
|
|
Class comprises mainly pointers to functions.
|
|
Those functions are used by the object itself or its descendant objects.
|
|
For example, GObject class is declared in `gobject.h` in GLib source files.
|
|
|
|
@@@include
|
|
class_gobject.c
|
|
@@@
|
|
|
|
I'd like to explain some of the members.
|
|
There's a pointer to the function `dispose` in line 22.
|
|
|
|
~~~C
|
|
void (*dispose) (GObject *object);
|
|
~~~
|
|
|
|
The declaration is a bit complicated.
|
|
The asterisk before the identifier `dispose` means pointer.
|
|
So, the pointer `dispose` points to a function which has one parameter, which points a GObject structure, and returns no value.
|
|
In the same way, line 23 says `finalize` is a pointer to the function which has one parameter, which points a GObject structure, and returns no value.
|
|
|
|
~~~C
|
|
void (*finalize) (GObject *object);
|
|
~~~
|
|
|
|
Look at the declaration of `_GObjectClass` so that you would find that most of the members are pointers to functions.
|
|
|
|
- 10: A function pointed by `constructor` is called when the instance is generated. It completes the initialization of the instance.
|
|
- 22: A function pointed by `dispose` is called when the instance destructs itself.
|
|
Destruction process is divided into two phases.
|
|
The first one is called disposing.
|
|
In this phase, the instance releases all the references to other instances.
|
|
The second phase is finalizing.
|
|
- 23: A function pointed by `finalize` finishes the destruction process.
|
|
- The other pointers point to functions which are called while the instance lives.
|
|
|
|
## TfeTextView class
|
|
|
|
TfeTextView class is a structure and it includes all its ancestors' class in it.
|
|
Let's look at all the classes from GObject, which is the top level object, to TfeTextView object, which is the lowest.
|
|
|
|
GObject -- GInitiallyUnowned -- GtkWidget -- GtkTextView -- TfeTextView
|
|
|
|
The following is extracts from the source files (not exactly the same).
|
|
|
|
@@@include
|
|
classes.c
|
|
@@@
|
|
|
|
- 105-107: This three lines are generated by the macro G\_DECLARE\_FINAL\_TYPE.
|
|
So, they are not written in either `tfe_text_view.h` or `tfe_text_view.c`.
|
|
- 2, 73, 106: Each derived class puts its parent class at the first member of its structure.
|
|
It is the same as instance structures.
|
|
- Class members in ancestors are open to the descendant class.
|
|
So, they can be changed in `tfe_text_view_class_init` function.
|
|
For example, the `dispose` pointer in GObjectClass will be overridden later in `tfe_text_view_class_init`.
|
|
(Override is an object oriented programming terminology.
|
|
Override is rewriting ancestors' class methods in the descendant class.)
|
|
- Some class methods are often overridden.
|
|
`set_property`, `get_property`, `dispose`, `finalize` and `constructed` are such methods.
|
|
|
|
TfeTextViewClass includes its ancestors' class in it.
|
|
It is illustrated in the following diagram.
|
|
|
|
![The structure of TfeTextView Class](../image/TfeTextViewClass.png){width=16.02cm height=8.34cm}
|
|
|
|
## Destruction of TfeTextView
|
|
|
|
Every Object derived from GObject has a reference count.
|
|
If an object A refers an object B, then A keeps a pointer to B in A and at the same time increases the reference count of B by one with the function `g_object_ref (B)`.
|
|
If A doesn't need B any longer, then A discards the pointer to B (usually it is done by assigning NULL to the pointer) and decreases the reference count of B by one with the function `g_object_unref (B)`.
|
|
|
|
If two objects A and B refer to C, then the reference count of C is two.
|
|
If A no longer needs C, A discards the pointer to C and decreases the reference count in C by one.
|
|
Now the reference count of C is one.
|
|
In the same way, if B no longer needs C, B discards the pointer to C and decreases the reference count in C by one.
|
|
At this moment, no object refers C and the reference count of C is zero.
|
|
This means C is no longer useful.
|
|
Then C destructs itself and finally the memories allocated to C is freed.
|
|
|
|
![Reference count of B](../image/refcount.png){width=15.855cm height=2.475cm}
|
|
|
|
The idea above is based on an assumption that an object referred by nothing has reference count of zero.
|
|
When the reference count drops to zero, the object starts its destruction process.
|
|
The destruction process is split into two phases: disposing and finalizing.
|
|
In the disposing process, the object invokes the function pointed by `dispose` in its class to release all references to other objects.
|
|
In the finalizing process, it invokes the function pointed by `finalize` in its class to complete the destruction process.
|
|
These functions are also called handlers or methods.
|
|
For example, dispose handler or dispose method.
|
|
|
|
In the destruction process of TfeTextView, the reference count of widgets related to TfeTextView is automatically decreased.
|
|
But GFile pointed by `tv->file` needs to decrease its reference count by one.
|
|
You must write the code in the dispose handler `tfe_text_view_dispose`.
|
|
|
|
@@@include
|
|
tfetextview/tfetextview.c tfe_text_view_dispose
|
|
@@@
|
|
|
|
- 5,6: If `tv->file` points a GFile, decrease its reference count.
|
|
`g_clear_object` decreases the reference count and assigns NULL to `tv->file`.
|
|
In dispose handlers, we usually use `g_clear_object` rather than `g_object_unref`.
|
|
- 8: invokes parent's dispose handler. (This will be explained later.)
|
|
|
|
In the disposing process, the object uses the pointer in its class to call the handler.
|
|
Therefore, `tfe_text_view_dispose` needs to be registered in the class when the TfeTextView class is initialized.
|
|
The function `tfe_text_view_class_init` is the class initialization function and it is declared in the replacement produced by `G_DEFINE_TYPE` macro.
|
|
|
|
~~~C
|
|
static void
|
|
tfe_text_view_class_init (TfeTextViewClass *class) {
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
|
|
object_class->dispose = tfe_text_view_dispose;
|
|
|
|
}
|
|
~~~
|
|
|
|
Each ancestors' class has been generated before TfeTextViewClass is generated.
|
|
Therefore, there are four classes and each class has a pointer to each dispose handler.
|
|
Look at the following diagram.
|
|
There are four classes -- GObjectClass (GInitiallyUnownedClass), GtkWidgetClass, GtkTextViewClass and TfeTextViewClass.
|
|
Each class has its own dispose handler -- `dh1`, `dh2`, `dh3` and `tfe_text_view_dispose`.
|
|
|
|
![dispose handlers](../image/dispose_handler.png){width=14.925cm height=4.455cm}
|
|
|
|
Now, look at the `tfe_text_view_dispose` program above.
|
|
It first releases the reference to GFile object pointed by `tv->file`.
|
|
Then it invokes its parent's dispose handler in line 8.
|
|
|
|
~~~C
|
|
G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
|
|
~~~
|
|
|
|
`tfe_text_view_parent_class`,which is made by `G_DEFINE_TYPE` macro, is a pointer that points the parent object class.
|
|
Therefore, `G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose` points the handler `dh3` in the diagram above.
|
|
And `gobject` is a pointer to TfeTextView instance which is casted as a GObject instance.
|
|
`dh3` releases all the references to objects in the GtkTextView part (it is actually the private area pointed by `prev`) in TfeTextView instance.
|
|
After that, `dh3` calls `dh2`, and `dh2` calls `dh1`.
|
|
Finally all the references are released.
|
|
|