GObject 信號(Signal)主要用於特定事件與響應者之間的連接,它與操作系統級中的信號沒有什麼關係。
一般在class_init時,由g_signal_new生成一個新信號句柄並綁定回調函數,也可以使用g_signal_connect連接對象和處理方式(回調函數),由g_signal_emit發出信號觸發。
guint
g_signal_new (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
guint class_offset,
GSignalAccumulator accumulator,
gpointer accu_data,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
...)
通過上面的函數模型可以看出,其參數個數是可變的。而這些參數有些是必須的,有些可設爲NULL,下面可以結合一個基於clutter的實例來解釋一下各個參數的含義(SECTION 1和2是從clutter源代碼中提取出來的定義示例):
1, const gchar *signal_name: 該參數是定義信號的名字,它由分隔符以及ASCII碼中的字母和數字構成,且第一個字符必須是字母,實例中定義的名字爲"myself-signal"。(分隔符可以是"-"或"_"——事實上,系統會先調用g_strdelimit把"_"轉化爲"-"再存儲signal_name。因此,在調用g_singal_emit_by_name時,detailed_signal參數中的分隔符必須是"-");
2, GType itype:該參數是signal所依附的類的在GType類型系統中註冊時得到的ID,也就是*_get_type()函數的返回值。而在clutter工程中,class_init時可使用G_OBJECT_CLASS_TYPE(klass)來獲得,實例中使用CLUTTER_TYPE_ACTOR獲取。(clutter _actor應該纔是stage和texture、text的基類);
3, GSignalFlags signal_flags:該參數是信號的屬性標記,共有七種,其中有提到"per-object handler",將在下面class_offset中介紹:
· G_SIGNAL_RUN_FIRST:調用回調函數時,"per-object handler"對應的回調函數將第一個調用;
· G_SIGNAL_RUN_LAST:調用回調函數時,"per-object handler"對應的回調函數將在用戶用g_signal_connect連接的回調函數之後調用,並在用戶用g_signal_connect _after連接的回調函數之前調用;
· G_SIGNAL_RUN_CLEANUP:調用回調函數時,"per-object handler"對應的回調函數將最後一個調用;
· G_SIGNAL_NO_RECURSE:信號發射時,如果信號的上次發射還沒有結束,那麼本次信號發射將不再進行,而只是使上次的信號發射重新開始。
· G_SIGNAL_DETAILED:信號名字可以使用"signal_name::detailed"的形式。
· G_SIGNAL_ACTION:程序員可以在代碼中自由地調用g_signal_emit族的函數來發射信號,而不需要把g_signal_emit族的函數放在一段代碼中再來調用。
· G_SIGNAL_NO_HOOKS:信號發射過程中不支持鉤子函數。
4, guint class_offset:該參數是itype對應的類的class結構中的一個函數指針相對於class結構的實例的首地址的偏移量。該函數指針所對應的函數常被稱爲"per-object handler","default (signal) handler"或"object methond handler",並將在信號發出後被調用(如調用g_signal_emit_by_name)。常配合宏G_STRUCT_OFFSET使用(該宏能夠返回結構體變量的成員相對於該結構體的變量的首地址的偏移量)。如SECTION1中,G_STRUCT_OFFSET (ClutterActorClass, paint),就是指在clutterActorClass類中,"per-object handler”爲 paint的函數的偏移地址。而如果將該參數設爲0,則表示該類沒有"per-object handler"。實例中由於在類中沒有定義相應的"per-object handler”,故設爲0;
5, GSignalAccumulator accumulator:該參數是一個函數指針,其對應的函數將在該信號的每個handler執行完以後執行。其函數模型可參見SECTION 3。該函數的返回類型爲gboolean。如果其返回值爲FASLE,則signal發射過程就會被中止(即不再調用後面的hander),否則會繼續下去。事實上,"delete-event"等帶有event後綴的signal就是利用了這一點——這些信號的某個回調函數如果返回了TRUE,則以後的回調函數就不會被調用。我的理解是,此函數的功能就是決定信號還要不要繼續往下走?(注意,如果該signal有accumulator,則回調函數類型(由c_marshaller反映)必須有返回值,否則該signal不能有accumulator,也即在調用g_signal_new時以NULL作爲該形參的實參)
6, gpointer accu_data:該參數將作爲用戶自定義參數傳入accumulator所指向的函數中。
7, GSignalCMarshaller c_marshaller:該參數是一個GSignalCMarshall類型的函數指針,其值反映了回調函數的返回值類型和額外參數類型(所謂“額外參數”,即指除回調函數中instance和user_data以外的參數)。
l 例如,g_closure_marshal_VOID_VOID說明該signal的回調函數爲以下的callback類型:typedef void (*callback) (gpointer instance, gpointer user_data);
l g_closure_marshal_VOID_POINTER----------typedef void (*callback) (gpointer instance, gpointer arg1, gpointer user_data);
l g_cclosure_marshal_VOID__CHAR ()---------void (*callback) (gpointer instance, gchar arg1, gpointer user_data);
l g_cclosure_marshal_VOID__INT ()-----------void (*callback) (gpointer instance, gint arg1, gpointer user_data);
Base.h
#ifndef _BASE_H_
#define _BASE_H_
//#include <gtk/gtk.h>
#include <glib-object.h>
#include <stdio.h>
typedef struct _TESTBase TESTBase;
typedef struct _TESTBaseClass TESTBaseClass;
#define TEST_TYPE_BASE (test_base_get_type())
//瀹炰緥綾誨瀷杞崲
#define TEST_BASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_BASE, TESTBase))
//瀹炰緥綾誨瀷鍒ゅ畾
#define TEST_IS_BASE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_BASE))
//綾葷粨鏋勮漿鎹?
#define TEST_BASE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_BASE, TESTBaseClass))
//綾葷粨鏋勫垽瀹?
#define TEST_IS_BASE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_BASE))
//鑾峯彇綾葷粨鏋?
#define TEST_BASE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_BASE, TESTBaseClass))
struct _TESTBase
{
GObject parent;
char szName[32];
};
struct _TESTBaseClass
{
GObjectClass classparent;
int iAction;
void (*basehello)(TESTBase *obj,int i);
};
//GType test_base_get_type(void);
void send_basehello_signal(TESTBase *pBase,int i);
#endif
Base.c
#include "Base.h"
#include <string.h>
typedef enum
{
HELLO_BASE_SIGNAL = 0,
LAST_BASE_SIGNAL
};
static int basesignals[LAST_BASE_SIGNAL];
G_DEFINE_TYPE(TESTBase,test_base,G_TYPE_OBJECT);
static void test_base_init(TESTBase *pBase)
{
memcpy(pBase->szName,"baseJob",sizeof(pBase->szName));
printf("test_base_init.\n");
}
static void test_base_class_destroy(GObject *object)
{
TESTBase *pTESTBase = NULL;
pTESTBase = TEST_BASE(object);
printf("test_base_class_destroy.\n");
}
static void test_base_class_finalize(GObject *object)
{
TESTBase *pTESTBase = NULL;
pTESTBase = TEST_BASE(object);
printf("test_base_class_finalize.\n");
}
static void test_base_hello_signal(TESTBase *pBase,int i)
{
printf("test_base_hello_signal[%p] [%d].\n",pBase,i);
}
static void test_base_class_init(TESTBaseClass *pBaseClass)
{
pBaseClass->iAction = 1;
//GTK_OBJECT_CLASS(pBaseClass)->destroy = test_base_class_destroy;
G_OBJECT_CLASS(pBaseClass)->finalize = test_base_class_finalize;
G_OBJECT_CLASS(pBaseClass)->dispose = test_base_class_destroy;
pBaseClass->basehello = test_base_hello_signal;
basesignals[HELLO_BASE_SIGNAL] = g_signal_new ("basehello",
G_TYPE_FROM_CLASS (pBaseClass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (TESTBaseClass, basehello),
NULL,
NULL,
g_cclosure_marshal_VOID__INT,
G_TYPE_NONE, 1, G_TYPE_INT);
printf("test_base_class_init.\n");
}
void send_basehello_signal(TESTBase *pBase,int i)
{
g_signal_emit (pBase, basesignals[HELLO_BASE_SIGNAL], 0,i);
}
Child1.h
#include "Base.h"
typedef struct _TESTChild1 TESTChild1;
typedef struct _TESTChild1Class TESTChild1Class;
#define TEST_TYPE_CHILD1 (test_child1_get_type())
//瀹炰緥綾誨瀷杞崲
#define TEST_CHILD1(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_CHILD1, TESTChild1))
//瀹炰緥綾誨瀷鍒ゅ畾
#define TEST_IS_CHILD1(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_CHILD1))
//綾葷粨鏋勮漿鎹?
#define TEST_CHILD1_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_CHILD1, TESTChild1Class))
//綾葷粨鏋勫垽瀹?
#define TEST_IS_CHILD1_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_CHILD1))
//鑾峯彇綾葷粨鏋?
#define TEST_CHILD1_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_CHILD1, TESTChild1Class))
struct _TESTChild1
{
TESTBase parent;
char szChild1Name[32];
};
struct _TESTChild1Class
{
TESTBaseClass classparent;
int iChild1Action;
void (*helloCb)(TESTChild1 *pBase,int i);
};
//GType test_base_get_type(void);
void send_hello_signal(TESTChild1 *pBase,int i);
//void test_hello_callback_connect(TESTChild1 *pBase,int i);
#endif
Child1.c
#include "Child1.h"
G_DEFINE_TYPE(TESTChild1,test_child1,TEST_TYPE_BASE);
typedef enum
{
HELLO_SIGNAL = 0,
LAST_SIGNAL
};
static int signals[LAST_SIGNAL];
static void test_child1_class_destroy(GObject *object)
{
TESTBase *pTESTBase = NULL;
pTESTBase = TEST_BASE(object);
printf("test_child1_class_destroy.\n");
G_OBJECT_CLASS(test_child1_parent_class)->dispose(object);
}
static void test_child1_class_finalize(GObject *object)
{
TESTChild1 *pTESTChild1 = NULL;
pTESTChild1 = TEST_BASE(object);
printf("test_child1_class_finalize.\n");
G_OBJECT_CLASS(test_child1_parent_class)->finalize(object);
//GTK_OBJECT_CLASS (test_child1_parent_class)->destroy (object);
}
static void test_child1_init(TESTChild1 *pBase)
{
printf("test_child1_init [%s].\n",pBase->parent.szName);
}
static void test_hello_callback(TESTChild1 *pBase,int i)
{
printf("TESTChild1[%p] recv hello signal[%d].\n",pBase,i);
}
static void test_child1_class_init(TESTChild1Class *pBaseClass)
{
printf("test_child1_class_init [%d].\n",pBaseClass->classparent.iAction);
//GTK_OBJECT_CLASS(pBaseClass)->destroy = test_child1_class_destroy;
G_OBJECT_CLASS(pBaseClass)->dispose = test_child1_class_destroy;
G_OBJECT_CLASS(pBaseClass)->finalize = test_child1_class_finalize;
pBaseClass->helloCb = test_hello_callback;
signals[HELLO_SIGNAL] = g_signal_new ("hello",
G_TYPE_FROM_CLASS (pBaseClass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (TESTChild1Class, helloCb),
NULL,
NULL,
g_cclosure_marshal_VOID__INT,
G_TYPE_NONE, 1, G_TYPE_INT);
}
void send_hello_signal(TESTChild1 *pBase,int i)
{
g_signal_emit (pBase, signals[HELLO_SIGNAL], 0,i);
}
main.c
#include "Child1.h"
static void test_hello_callback_connect(TESTChild1 *pBase,int i,gpointer user_data)
{
int j = 0;
printf("TESTChild1[%p] recv hello signal connect[%d].\n",pBase,i);
for(j = 0;j < 2047483648;j++);
sleep(3);
printf("TESTChild1[%p] recv hello signal connect[%d] end.\n",pBase,i);
}
static void test_hello_callback_connect1(TESTChild1 *pBase,int i,gpointer user_data)
{
int j = 0;
printf("TESTChild1[%p] recv hello signal connect1[%d].\n",pBase,i);
for(j = 0;j < 2047483648;j++);
sleep(3);
printf("TESTChild1[%p] recv hello signal connect1[%d] end.\n",pBase,i);
}
int main (void)
{
g_type_init ();
int i;
TESTChild1 *P,*P1,*P2;
#if 1
P = g_object_new (TEST_TYPE_CHILD1, NULL);
P1 = g_object_new (TEST_TYPE_CHILD1, NULL);
P2 = g_object_new (TEST_TYPE_CHILD1, NULL);
printf("g_object_new P[%p] P1[%p] P2[%p].\n",P,P1,P2);
g_signal_connect(G_OBJECT(P), "hello",
G_CALLBACK (test_hello_callback_connect1),NULL);
g_signal_connect(G_OBJECT(P), "hello",
G_CALLBACK (test_hello_callback_connect),NULL);
printf("start send signal.\n");
send_hello_signal(P,1);
send_hello_signal(P1,2);
send_hello_signal(P2,3);
send_basehello_signal(P1,4);
printf("end send signal.\n");
g_object_unref (P);
g_object_unref(P1);
g_object_unref(P2);
#else
P = g_object_new (TEST_TYPE_CHILD1, NULL);
g_object_unref (P);
printf("new second object.\n");
P = g_object_new (TEST_TYPE_CHILD1, NULL);
g_object_unref (P);
#endif
return 0;
}
編譯命令:
gcc -Wall Base.c Child1.c main.c -o test $(pkg-config --cflags --libs gobject-2.0)
運行結果:
./test
test_base_class_init.
test_child1_class_init [1].
test_base_init.
test_child1_init [baseJob].
test_base_init.
test_child1_init [baseJob].
test_base_init.
test_child1_init [baseJob].
g_object_new P[0x23e91e0] P1[0x23e9240] P2[0x23e92a0].
start send signal.
TESTChild1[0x23e91e0] recv hello signal connect1[1].
TESTChild1[0x23e91e0] recv hello signal connect1[1] end.
TESTChild1[0x23e91e0] recv hello signal connect[1].
TESTChild1[0x23e91e0] recv hello signal connect[1] end.
TESTChild1[0x23e91e0] recv hello signal[1].
TESTChild1[0x23e9240] recv hello signal[2].
TESTChild1[0x23e92a0] recv hello signal[3].
test_base_hello_signal[0x23e9240] [4].
end send signal.
test_child1_class_destroy.
test_base_class_destroy.
test_child1_class_finalize.
test_base_class_finalize.
test_child1_class_destroy.
test_base_class_destroy.
test_child1_class_finalize.
test_base_class_finalize.
test_child1_class_destroy.
test_base_class_destroy.
test_child1_class_finalize.
test_base_class_finalize.
總結:
1、信號發送後只有發送函數g_signal_emit指定的實例會有響應調用回調函數。
2、同一個實例上面註冊同一個信號的多個回調函數時,響應的順序取決於調用g_signal_connect註冊的順序。g_signal_connect和g_signal_new直接的回調函數調用順序取決於g_signal_new的第三個參數。
3、基類中的註冊的信號,向子類發送信號後仍然會響應。