GObject 參考手冊(12)--訊號
訊號
GObject的訊號與標準的UNIX訊號無關:它們連線應用程式相關的任一數量監聽程式的事件。例如:在GTK+中,每個使用者事件(按鍵或滑鼠移動)是從X Server和在一個給出的物件例項的訊號發射形式下產生的事件接收到的。
每一個訊號同能被髮送的型別一起註冊到型別系統中:當該型別的使用者註冊了一個closure到被呼叫的訊號上時,會通知連線(connect)到了給出的型別例項訊號。使用者也可以通過它們自己傳送訊號,或從連線到訊號的closures內停止訊號的傳送。
當某個訊號被給出的型別例項發射時,所有連線到這個型別例項上這個訊號的closures都將被呼叫。所有連線到這個訊號的closures
訊號註冊
它的引數定義如下:
signal_name: 它是一個可以唯一標識一個所給訊號的字串
itype: 可以傳送訊號的例項型別
signal_flags: 在連線到訊號的closures中部分定義的呼叫順序
class_closure: 這是訊號的預設closure:如果在訊號發射時為非空(not NULL),它將在信 號發射時被呼叫。這個時候,被呼叫的closure會被當成其他的連線到那個訊號的closures,並部分依賴於signal_flags.
accumulator: 這是一個函式指標,它在每一個closure
accumulator_data: 這個指標將會在訊號發射時傳遞給每一個accumulator.
c_marshaller: 這是為連線到這個訊號的任一一個colsure的預設C的marchaller.
return_type: 這是訊號返回值的型別
n_params: 這是訊號使用的引數個數
param_types: 這是一個GTypes的陣列,它表示訊號每個引數的型別。這個陣列的長度由n_params指定。
從上面的定義可以看出,一個訊號基本上就是一個能連線到這個訊號的
訊號連線
如果想用closure連線一個訊號,可以有三種選擇:
l你可以在訊號註冊時註冊一個class closure: 這是一個全系統操作。例如:這個class_closured 會在所有支援型別的例項每一個訊號傳送期間被呼叫。
l你可以使用它是覆蓋一個給出型別的class_closure. 僅僅在訊號註冊型別的派生型別上呼叫這個函式是可能的。這個函式僅對語言邦定有用。
l你可以使用函式族註冊一個closure.這是一個例項相關的操作:這個closure僅僅在指定例項的指定訊號發射時才會被呼叫。
連線一個不同型別的回撥函式到指定的訊號是可行的:無論哪種例項的訊號無論在什麼時候發射都會呼叫發射的鉤子函式。發射鉤子函式被使用做為在一個應用中得到所有的mouse_clicked發射的例子發出小的滑鼠點選聲音。發射鉤子函式用連線,用去除連線。
訊號發射
lGvalues陣列instance_and_params包含了輸入引數到訊號的列表。陣列的第一個元素是例項指標,它是呼叫這個訊號的例項。第二個元素包含了到這個訊號的引數列表。
lsignal_id標識呼叫的訊號。
ldetail標識了呼叫訊號的具體細節,detail是一種有魔力的表示或引數,它的傳遞是在訊號發射時,它被連線到這個訊號的closures使用來濾出不想要的訊號發射。在大多數情況下,你可以安全地設定這個值為0。對這個引數的更詳細的描述請參見
lreturn_value如果沒有累加器被指定,這個引數儲存的是在發射期間最後一個被呼叫的closure的返回值。如果一個累加器(accumulator)在訊號建立時被指定,這個累加器被使用來計算return_value,作為在發射期間所有被呼叫的closures的返回值的函式。如果在發射期間沒有closure被呼叫,return_value仍被初始化為0或null。
在內部,Gvalue陣列被傳遞到正確的發射函式,signal_emit_unlocked_R
(在gsignal.c
中實現
)。訊號發射可以分解為5步:
lRUN_FIRST:如果G_SIGNAL_RUN_FIRST標誌在訊號註冊期間被使用,並且如果這個訊號存在一個class_closure, class_closure被呼叫。然後跳到EMISSION_HOOK狀態
lEMISSION_HOOK:如果任一的發射鉤子函式被增加到這個訊號,他們將按照增加的順序被呼叫。累加返回值並跳到HANDLER_RUN_FIRST狀態。
l
HANDLER_RUN_FIRST:如果任一的closure用函式族被連線,如果他們沒有被鎖定(用函式族),他們就會被連線的第一個到最後一個的順序執行。然後跳到RUN_LAST狀態。
lRUN_LAST
:如果
G_SIGNAL_RUN_LAST標誌在註冊期間被設定,且一個class_closure被設定,它會在這裡被呼叫。然後跳到HANDLER_RUN_LAST狀態。
lHANDLER_RUN_LAST:如果用g_signal_connect_after
函式族把
任一的closure連線,如果他們沒有在HANDLER_RUN_FIRST期間被呼叫,以及如果沒有被鎖定,他們就會在這裡按照連線順序執行。然後跳到RUN_CLEANUP狀態。
lRUN_CLEANUP:如果G_SIGNAL_RUN_CLEANUP標誌在註冊期間被設定,並且一個class_closure被設定,它會在這裡呼叫。在這裡,整個訊號發射全部完成。
如果在發射期間的任一一種狀態下(除了RUN_CLEANUP狀態),closures中的一個或發射鉤子函式用g_signal_stop
停止訊號發射,發射就會跳到CLEANUP狀態。
如果在發射期間的任一一種狀態下,在同樣的例項上closures的一個或發射鉤子函式發射相同的訊號,發射會從RUN_FIRST狀態重新開始。
在每一個closure呼叫後(除了EMISSION_HOOK和CLEANUP),在所有狀態累加器函式被呼叫。它累加closure的返回值到訊號的返回值然後返回TRUE或FALSE。如果在任一狀態下,沒有返回TRUE,發射會跳到CLEANUP狀態。
如果沒有提供累加器函式,返回值被返回的最後一個執行的處理程式返回。
detail引數
所有涉及到訊號發射或訊號連線的函式都有名為detail的引數。有時,這個引數是被API隱藏了,但是它卻總是以一種形式存在。
下面的三個主要的連線函式,僅僅只有一個有清楚的作為Gquark的detail引數:
另外的兩個函式在訊號名的標識中隱藏了detail引數。
它們的detailed_signal引數是一個字串,它連線訊號的名稱。然爾,字串的格式是被構造成像signal_name::detail_namer的格式。 名稱為notify::cursor_position的訊號連線將真正連線到用cursor_position名字命名為notify的訊號。
在內部,如果detail字串存在,則被轉換為GQuark。
下面的四個訊號發射函式中,3個有作為GQuark的顯式的detail引數:
第四個函式在訊號名引數中隱藏了此引數。
引數detailed_signal的格式是與被 g_signal_connect函式使用的格式signal_name::detail_name相同的。
如果detail被髮射函式的使用者提供,在發射時被使用來與提供detail的clousures匹配。如果這個closures的detail與使用者提供的detail不匹配,他們就不會被呼叫(即使它們被連線到了訊號,該訊號被髮送了)。
這種完全可選的過濾機制主要用於訊號的最優化。因為訊號常常被許多不同的原因傳送:客戶可以在closure的排程程式碼執行前濾出它們感興趣的事件。例如:GObject的notify訊號是非常的有用:在一個GObject物件上,無論什麼時候修改了一個屬性,GObject會關聯修改屬性的名字到這個訊號發射的detail中,而不是直接發射notify訊號。這就允許希望只有一個屬性改變時被通知的客戶端在接收訊號前能濾出大部分的事件。
作為一個簡單的規則,使用者可以也應該把detail引數設定為0:這將完全禁止可選的過濾。
-------------------------------------------------------
[8] James (again!!) gives a few non-trivial examples of accumulators: “ For instance, you may have an accumulator that ignores NULL returns from closures, and only accumulates the non-NULL ones. Another accumulator may try to return the list of values returned by the closures. ”
[9] A GQuark is an integer which uniquely represents a string. It is possible to transform back and forth between the integer and string representations with the functions g_quark_from_string
and g_quark_to_string
.