GObject

**GLib 物件系統**，或者說 **GObject**，是一個在 LGPL 下發布的自由軟體庫，它提供了一個輕便的物件系統並支援透明的多語言互通。GObject 被設計為可以直接使用在 C 程式中，也可以被封裝至其他語言，例如 C++，Java，Python，以及可以生成 C 代碼的 Vala（由此大大簡化了 GObject 代碼的書寫）等等。

## 歷史

GObject 僅依賴於 GLib 和 libc。它是 GNOME 的基石並且在 GTK+、Pango、輔助功能工具箱，和大多數 GNOME 的進階庫和應用程式中被廣泛使用。在 GTK+ 2.0 之前，GObject 代碼是 GTK + 的一部分。（現在 GObject 這個名字已經不在 GTK + 中了──取代它的基本類型叫做 `GtkObject`。）

由於物件系統適用的範圍更廣，較有一般性，所以在 GTK+2.0 發布時，物件系統被分離了出來，改放到了另一個函式庫。GtkObject 在 Gtk 演進的過程里，大部分與 GUI 不相關的部份都移到了 GObject 里，造就了新的共享基礎類型 GObject 的誕生。從 2002 年 3 月 11 日（也就是 GTK+ 2.0 發布的日子）開始，GObject 就一直以一個分離的函式庫的形式存在。GObject 函式庫現在被許多非 GUI 的程式，像命令列應用程式、伺服器應用程式等，所使用。

## 與 GLib 的關係

雖然 GObject 有屬於它自己的文件集，與獨立的函式庫，但原始碼卻是在 Glib 的原始碼樹裡，並且與 GLib 一起發行。因此，GObject 使用與 GLib 一樣的版本號碼，而且一般 Linux Distro 的作法也是把 GObject 包在 GLib 裡（舉例來說，Debian 把 GObject 放在 libglib2.0 套件家族裡）。

## 類型系統

GObject 框架的基層主要依靠泛型與動態型別，稱作 GType。GType 系統保留所有物件的執行時期描述以讓 glue code 能方便地與其他語言作連結。類型系統可以處理任何單一繼承的類別架構以及非類別的型別，如不透明指標、字串跟各種長度的整數與浮點數。

類型系統知道如何複製、指派和釋放屬於任何已註冊類型的值。這對像整數這種不是參照計數（reference-counted）的型別來說是很瑣碎的事情，但是對於其他是參照計數的複雜物件來說，是必要的。當類型系統複製了一個參照計數的物件，它僅僅只是增加該物件的參照計數，對複製一個複雜、不是參照計數的物件時，則是以組態記憶體的方式建立副本。

這項基本的能力被實作在 GValue，GValue 是一個泛型容器的型別，裡面可以用來儲存註冊在類型系統裡的型別的值。這樣的容器在與動態型別語言溝通時，特別地有用。

### 基礎類型

沒有任何關聯類別的類型被稱作非類別的。這些型別相當於根類別，也就是基礎類型，可以被其他類型繼承。

在 GLib 2.9.2 裡，非類別的內建基礎類型有：

* 空類型，對應到 C 的 void (G\_TYPE\_NONE）;
* 對應到 C 的有號、無號 char、int、long 和 64 位元整數 long long （G\_TYPE\_CHAR, G\_TYPE\_UCHAR, G\_TYPE\_INT, G\_TYPE\_UINT, G\_TYPE\_LONG, G\_TYPE\_ULONG, G\_TYPE\_INT64, and G\_TYPE\_UINT64）;
* 布林類型（G\_TYPE\_BOOLEAN）;
* 列舉型別和 "旗標" 型別，都對應到 C 的列舉類型，但後者只使用在位元欄位上（G\_TYPE\_ENUM and G\_TYPE\_FLAGS）;
* 單精度與雙精度的 IEEE 浮點數，對應到 C 的 float 跟 double (G\_TYPE\_FLOAT and G\_TYPE\_DOUBLE）;
* 字串類型，對應到 C 的 char \* (G\_TYPE\_STRING);
* 不透明指標，對應到 C 的 void \*（G\_TYPE\_POINTER）。

類別內建的基礎類型有：

* GObject 實體的基底類別類型，標準類別繼承樹的根（G\_TYPE\_OBJECT）
* 基底介面類型，跟基底類別類型很相似，但卻是標準介面繼承樹的根（G\_TYPE\_INTERFACE）
* 包裝了結構的類型，被用來包裝簡單的值物件或外來物件在參照計數的 "盒子" 裡（G\_TYPE\_BOXED）
* " 參數規格物件的類型，用在 GObject 裡作為描述物件屬性的元資料（G\_TYPE\_PARAM）。

可以被系統自動實體化的類型被稱作可實體化（instantiable）。這些類型的一個重要特色就是實體的第一個位元組永遠包含一個指標，指向連結到該實體類型的類別結構（虛擬表格的表單）。為此，任何可被實體化的類型應該是類別。相對地，任何非類別類型（像整數或字串）必須是不可實體化。另外，大部分類別類型是可實體化的，但某些類型，像介面類型，就不是。

### 衍生類型

從內建 GObject 基礎類型衍生下來的，主要有四種類型：

**列舉類型和 "旗標" 類型**
: 一般來說，列舉類型或旗標其實都是整數，以相對口語的單字來表示特定的數值，一般都作為物件屬性的類型。GLib 提供了 `glib-mkenums` 工具，來自動化產生的過程，並產生必要的代碼。

**Boxed 類型**
: 有些資料結構很簡單，並不需要是一個類別。舉例來說，我們可能有個類別，裡面需要加個 `background-color` 屬性，他的值應該是某個結構的實體，所以代碼看起來像是這樣：`struct color { int r,g,b; }`。要避免繼承 `GObject` 的話，我們可以建立一個 boxed 類型來呈現這樣的結構，並且提供複製和釋放的處理函式給 GType。GObject 提供了簡便的方法，可以讓你為 GLib 資料類型作包裝。

**不透明的指標類型（Opaque pointer types）**
: 有時候，物件既不需要複製也不需要作參照計數或釋放。這樣的物件在 GObject 裡，可以當作是不透明指標（`G_TYPE_POINTER`）。通常被用來參考到特定種類的物件。

**類別與介面類型**
: GObject 應用程式裡的大部分類型都是類別，直接或間接地繼承自根類別：`GObject`。是的，GObject 裡也可以使用類似 Java 的介面（interface），雖然很少被使用到。很少使用到的原因可能是因為介面是在 GLib 2.4（在 2004 年 3 月 16 日釋出）才被加進去。GIMP 就有使用到 GObject 的介面。

## 訊息系統

GObject 訊息系統由兩個互補的部份所組成：closures 與訊號。

**Closures**
: GObject closure 是 callback（回呼）的一般化版本。支援現存已經用 C/C++ 或其他語言寫好的 closure（當提供繫結時）。這允許以例如 Python 或 Java 等寫好的代碼被 GObject closure 調用。

**訊號**
: 訊號（Signal）是 closure 被調用的主要機制。物件向類型系統註冊訊號 listener，在給定的訊號與給定的 closure 間指定對應關係。當註冊的訊號被發出時，對應的 closure 就會被調用。在 GTK + 裡，所有內定的 GUI 事件（像滑鼠移動和鍵盤動作）都會為 listeners 產生 GObject 訊號以進行運作。

## 類別實作

每個 GObject 類別必須包含至少兩個結構：類別結構與實體結構。

**類別結構**
: 類別結構相當於 C++ 類別的 vtable。第一個元素必須是父類別的類別結構。裡面包含一組函式指標，也就是類別的虛擬方法。放在這裡的變數，就像是 C++ 類別裡的 const 或類別層級的成員。

**實體結構**
: 每個物件實體都將會是這個實體結構的副本，同樣地，第一個元素，必須是實體結構的父類別（這可以確保每個實體都有個指標可以指向類別結構，所有的基礎類別也同樣如此），在歸入父類別之後，可以在結構內放其他的變數，這就相當於 C++ 的成員變數。

C 結構沒有像 "public", "protected" 或 "private" 等的存取層級修飾，一般的方法是藉著在實體結構裡提供一個指向私有資料的指標，照慣例稱作 \_priv。私有的結構可以宣告在公有的表頭檔案裡，然後把實體的定義寫在實作的檔案中，這樣作，對使用者來說，他並不知道私有資料是什麼，但對於實作者來說，卻可以很清楚的知道。如果私有結構也註冊在 GType 裡，那麼物件系統將會自動幫它組態空間。

GObject 框架最主要的不利點在於太過於冗長。像是手動定義的類型轉換巨集和難解的型別註冊咒語等的大量模板代碼都是建立新類別所必要的。GObject Builder 或 GOB2 這些工具試圖以提供模板語法來解決這個問題。以 GOB2 寫的代碼必須事先處理過才能編譯。另外，Vala 可以將 c# 的語法轉換成 C，並編譯出獨立的二進制檔。

## 用途

C 與 GObject 的組合被使用在許多成功的自由軟體專案上，像是 GNOME 桌面、GTK 與 GIMP 影像處理軟體。

儘管許多的 GObject 應用程式完全以 C 來撰寫，但 GObject 系統可以很好地對應到許多語言，像 C++、Java、Ruby、Python 和.NET/Mono 等的原生物件系統。所以在為已經使用 GObject 框架寫好的函式庫建立語言繫結時，通常比較不會那麼痛苦。

以 C 來撰寫 GObject 代碼時，卻是相對痛苦。學習曲線十分陡峭，有高階物件導向語言經驗的開發者可能會發現以 C 撰寫 GObject 代碼相當的乏味。舉例來說，要繼承一個類別（即使是繼承 GObject）可能就需要撰寫和（或）複製上百行的樣板代碼。雖然如此，不可否認地，GObject 可以為 C 的代碼提供物件導向的功能。

GObject 應用程式在執行時期為類別和介面所建立的元物件提供了互相操作的良好支援。可互相操作的能力被使用在語言繫結上，還有使用者介面設計應用程式（如 Glade）上。Glade 允許載入提供放了 Widget（元件，衍生自 GObject）的函式庫，並且取得類別的屬性列表、類型資訊以及檔案字串。

## 與其他物件系統比較

GObject 為 C 提供了近乎完整的物件系統，所以使用 C 語言配合 GObject，可以做為使用其他從 C 分支出去的語言，像 C++ 和 Objective-C，的替代方案。（不過 C++ 和 Objective-C 其實各自有很多其他特色，而不是只有差在物件系統。）　最明顯也最簡單的不同，是 GObject 跟 Java 一樣，不支援多重繼承。

另一個重要的不同，GObject 僅僅只是一個函式庫，而 C++ 和 Objective-C 的編譯器還額外提供了新的語法或特性。

就目前的 C++ 編譯器來說，並沒有標準的 ABI 可以在所有的 C++ 編譯器運行（除了 Windows，Windows 上有 COM 可以處理），以 A 這個 C++ 編譯器所編譯出來的函式庫，並不一定能被以 B C++ 編譯器所編譯的程式呼叫。如果需要這樣的相容性，C++ 的方法必須要輸出為 C 的函式，這樣就無法享受 C++ 帶來的好處了。這主要是因為不同的 C++ 編譯器使用了不同的名稱特殊處理（Name mangling）以確保輸出符號的獨一性。(這是必要的，舉例來說，不同的類別可能會有一樣名稱的成員函式、被覆載許多次的函式名稱，或者出現在多個命名空間但同名的函式，但在輸出為目的檔時，這些代碼都是獨立的，如果名稱都一樣，會被視為同一份代碼，因此需要對名稱作特殊處理。）對照 C 來看，C 不支援任何形式的覆載或名稱特殊處理，C 函式庫的作者永遠只能使用明確的前置名以確保輸出名稱的全域獨一性。因此，以 C 撰寫的 GObject 基底的函式庫將不會有名稱特殊處理的問題，也不會受到編譯器的影響。

最後，"訊號"（signal）可以說是更容易被發現的相異點了（在其他語言被稱作事件）。這當然是因為 GObject 最早被設計用在 GUI 工具箱上。的確，許多物件導向語言都已經有現成的訊號函式庫，但 GObject 是被內建在物件系統裡的。因此，GObject 應用程式與其他非 GObject 應用程式比起來，會傾向於去使用訊號來實作。這使得 GObject 元件，相較於純 C++ 或 Java 寫的元件，更易於封裝，也容易被重複使用。不過該注意的是，在幾乎所有的平台都可以使用訊號，但有時其實需要額外的函式庫支援，例如可用於 C++ 的 Boost.Signals2。

## 參照

## 參見

* Vala — 基於 GObject 的程式語言，具有 C# 語法和到 C 的源到源編譯器。
* Genie — Vala 編譯器的可替代的 Python 風格語法的解析器。

## 外部連結

* The GObject Reference Manual （and tutorial） （頁面存檔備份，存於網際網路檔案館）
* GObject Tutorial Aug 2004
* GOB2—the GObject Builder （頁面存檔備份，存於網際網路檔案館）
* Vala Homepage（頁面存檔備份，存於網際網路檔案館）