跳转到内容

GObject

本页使用了标题或全文手工转换
维基百科,自由的百科全书

这是本页的一个历史版本,由X4base留言 | 贡献2012年1月1日 (日) 06:08 類型系統:​ 修正筆誤)编辑。这可能和当前版本存在着巨大的差异。

一个典型的GObject类的初始化代码。这个例子显示了一个属性 file-size 被加到一些类中。

GLib对象系统,或者说GObject,是一个在LGPL下发布的自由软件库 ,它提供了一个轻便的对象系统并支持透明的多语言互通。GObject被设计为可以直接使用在 C 程序中,也封装至其他语言。

历史

GObject仅依赖于GLiblibc。它是GNOME的基石并且在GTK+PangoAccessibility Toolkit和大多数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 有屬於它自己的文件 [1] 與獨立的函式庫,但原始碼卻是在 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 [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 [3] 工具來自動化產生的過程,並產生必要的代碼。
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 分支出去的語言,像 C++ 和 Objective-C 比起來,其實有很大的不同。先不管 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 元件更易於封裝,也容易被重複使用。

外部連結