本書是關于Gecko和基于Gecko應用程式來開發XPCOM元件的。簡介部分探讨元件的概念,第一章你将編譯簡單的代碼并注冊到Mozilla中,此時會探讨元件和子產品之間的關系,XPCOM接口以及注冊的過程。
假定讀者熟悉C++中的繼承和封裝,很多例子是javascript的,它用來做完腳本對象在Mozilla中通路XPCOM元件,是以熟悉它也是很好的。
XPCOM表示跨平台元件對象模型,類似與微軟的COM.若你有這方面的經驗,可以直接運用上來。
全書貫穿一個叫WebLock的例子來講解XPCOM元件的開發,步驟如下:
1) 為元件開發基本的子產品代碼
2) 使用C++宏,特别的字元串類和智能指針來優化代碼
3) 定義元件的功能,為這些功能建立XPIDL接口,為WebLock元件接口開發特定的實作代碼。
4) 結束實作WebLock元件:nsIContentPolicy,檔案I/O,加鎖等
5) 為WebLock元件建立使用者接口
6) 打包WebLock以便釋出和安裝。
元件在運作時組裝到一起,建構時和其他元件無關,為了允許一個應用程式中的元件之間的互操作,XPCOM将元件的接口和實作相分離。XPCOM提供了一些工具和庫來加載和操作這些元件,一些服務來幫助開發者編寫子產品化的跨平台代碼,以及版本支援,是以元件可以在不破壞應用程式或重新建立應用程式的情況下替換和更新,還具有良好的可複用性。
提供的其他功能有:元件管理,檔案抽象化,對象消息傳遞,記憶體管理。元件一般以可複用的二進制庫形式釋出(例如windows上的DLL),它可以包括一個或多個元件,若在一個二進制庫中有兩個以上相關的元件,這個庫就叫做子產品。
接口就是元件的通信通道,它不是什麼新概念,我們寫”hello world”時就用到了接口,那時接口是我們寫的應用程式代碼和列印代碼之間,我們用stdio這個接口來列印字元串。XPCOM來寫”hello world”的話,唯一的差別是它是在運作時查找這個列印函數,但在編譯時并不知道stdio。
元件和面向接口程式設計的兩個基本問題是元件生命周期和接口查詢(在運作時識别元件支援哪些接口)。nsISupports基接口是XPCOM中所有接口的祖先接口,它提供了這兩個問題的解決方案。
XPCOM中,接口是引用計數的。元件必須跟蹤用戶端引用它的數目,并且當引用數為0時删除自己。
當一個元件被建立後,其内部的一個整數跟蹤這個引用數。當用戶端執行個體化元件時這個引用計數自動加1,在元件的整個生命周期中,這個引用計數加加減減,始終保持大于0,某個時候,所有的客戶都對元件不感興趣了,引用計數到達0,元件就删除自己。
若由客戶來負責接口的計數,那麼如果它忘記對引用計數減1的話就會出問題,接口将不會被釋放,進而導緻記憶體洩露。nsISupports提供了接口發現和引用計數的基本功能。其成員函數QueryInterface,AddRef,Release,提供了從一個對象擷取指定接口,增加引用計數,當對象不再被使用時釋放它的功能。
Class Sample : public nsISupports
{
Private:
Nsrefcnt mRefCnt;
Public:
Sample();
Virtual ~Sample();
NS_IMETHOD QueryInterface(const nsIID &aIID,void **aResult);
NS_IMETHOD_(nsrefcnt) AddRef(void);
NS_IMETHOD_(nsrefcnt) Release(void);
};
Sample()
// initialize the reference count to 0
mRefCnt = 0;
}
Sample::~Sample()
// typical, generic implementation of QI
NS_IMETHODIMP Sample::QueryInterface(const nsIID &aIID,
void **aResult)
if (aResult == NULL) {//空指針
return NS_ERROR_NULL_POINTER;
*aResult = NULL;
if (aIID.Equals(kISupportsIID)) {
*aResult = (void *) this;
if (*aResult == NULL) {//接口不支援,書上這裡好像錯了
return NS_ERROR_NO_INTERFACE;
// add a reference
AddRef();//引用計數加1
return NS_OK;
NS_IMETHODIMP_(nsrefcnt) Sample::AddRef()
return ++mRefCnt;
NS_IMETHODIMP_(nsrefcnt) Sample::Release()
if (--mRefCnt == 0) {//目前是最後一個引用元件的客戶
delete this;
return 0;
// optional: return the reference count
return mRefCnt;
XPCOM沒有使用C++的RTTI,它通過QueryInterface接口來将對象轉換到其支援的接口。每個接口都被賦予一個辨別符,它通過一個“uuidgen”的工具産生。UUID是一個唯一的,128位的數。在接口中,它叫做IID(對于元件來說,它叫契約ID).
當一個客戶想知道對象是否支援某個接口,它就将IID通過QueryInterface傳給對象,若那個對象支援此接口,它就對其自身引用計數加1并傳回一個到那個接口的指針。若不支援,則傳回一個錯誤資訊。
class nsISupports {
public:
long QueryInterface(const nsIID & uuid,void **result) = 0;
long AddRef(void) = 0;
long Release(void) = 0;
QueryInterface的第一個參數是一個名叫nsIID的類的引用,它是IID的簡單封裝。它有三個方法,Equals,Parse,ToString. Equals最重要,它用來在接口查詢的過程中比較兩個nsIID。
當你實作nsIID類時,當客戶使用nsISupports IID調用QueryInterface時,必須確定其方法傳回一個有效值
前面的例子中可以簡單地使用c語言風格的轉換,但要轉換為所需要的類型時要複雜些,因為你必須傳回與所需要的接口對應的虛函數表的接口指針。當繼承層次中有二義性時會出問題。
除了前面讨論的IID接口辨別符外,XPCOM還使用了兩種辨別符來差別類群組件:1)CID.2)契約ID.
CID和IID類似,也是128位數,唯一辨別一個類或元件。用于nsISupports的
CID:00000000-0000-0000-c000-000000000046
#define SAMPLE_CID \
{ 0x777f7150, 0x4a2b, 0x4301, \
{ 0xad, 0x10, 0x5e, 0xab, 0x25, 0xb3, 0x22, 0xaa}}
你還會經常看到NS_DEFINE_CID,這個宏定義了一個CID值常量。
static NS_DEFINE_CID(kWebShellCID, NS_WEB_SHELL_CID);
CID有時候也叫做類辨別符,若CID引用的類實作2個以上的接口,則CID確定當它釋出或當機時實作了接口集合的全部接口。
契約ID是給人看的,CID或者契約ID可以用來從元件管理器那擷取一個元件,"@mozilla.org/network/ldap-operation;1",格式是域名/子產品/元件名/版本号
和CID一樣,契約ID引用到一個實作而不是接口,引用到接口是IID幹的。但契約ID并不綁死在一個特定的實作,而CID卻綁死了。契約ID隻是指明了它想實作的一個特定的接口集合,而任何數目的CID都可以進來滿足這個需求。契約ID和CID的這個差別使得它可以用來override元件。
工廠模式可以用來封裝對象的建立。工廠的目标是在不向客戶暴露對象實作和初始化的情況下建立對象。
int New_SomeInterface(SomeInterface** ret)
// create the object
SomeClass* out = new SomeClass();
if (!out) return -1;
// init the object
if (out->Init() == FALSE)
delete out;
return -1;
// cast to the interface
*ret = static_cast<SomeInterface*>(out);
return 0;
XPCOM中,工廠是 nsIFactory接口的實作,使用了工廠設計模式來封裝對象的建構和初始化過程。
本文轉自Phinecos(洞庭散人)部落格園部落格,原文連結:http://www.cnblogs.com/phinecos/archive/2008/05/29/1210298.html,如需轉載請自行聯系原作者