描述符Descriptors
<上>概念和使用
接觸Symbian已經一個半月多了,自從上個月熟悉了框架之后,一直都不敢再寫什么東西了,因為沒有經歷過代碼怎么可能寫得出東西呢?起筆猶豫了很久,打算涉足Symbian與標準C++的一個不同點——描述符。希望自己能夠借這個機會搞清楚描述符這個東西。
一、總介
由于手機系統的資源區別于PC,為此為了更好的在內存受限設備上處理內存緩沖,Symbian提供了獨特的描述符,用以存儲和操作字符串、以及管理二進制數據和其它串行化的復雜對象(serialized compound objects)。
Symbian OS的很多API調用的參數都是描述符,同時Symbian也為描述符提供了很多的操作函數。但是描述符本身其實就是一個封裝了數據及其長度的內存塊類。作為字符串處理類,它與標準C++的以'\0'結束符的字符串有區別,即它沒有結束符;描述符在處理字符串和二進制數據時又有不同:首先Symbian使用Unicode,所以字符串通常存于16位描述符中,而二進制數據存儲于8位描述符中(通常在底層通信中用的都是8位的描述符);其次如果描述符中包括二進制數據,則描述符的字符串操作方法不可用(我的理解是可以用,但是不能當字符串來用,所以也就沒有意義了。至于處理串行化對象,本人沒有接觸所以暫時略過)。
二、描述符類型分類及其相互關系
描述符主要有四類,但是我們通常將文字常量也作為描述符的一類,所以就有了五類,以下就是常見分類:
· 抽象類(Abstract):(TDes、TDesC、Tdes8、TdesC8),其他描述符的基類,僅提供接口和基本功能,不能被實例化,一般只用作函數的參數。
· 文字常量(Literal):(TlitC、_LIT()),用于存儲文字字符串(literal string),即C中字符串常量,通常使用_LIT()這種方式(當然還有_L()和_L8()的描述方式,但都不提倡用)。
· 棧類(Buffer):(Tbuf、TbufC、 Tbuf8、TbufC8),數據存儲于棧上,最基本的描述符變量類型,大小在編譯時確定,包含描述符本身數據,使用最為普遍。
· 堆類(Heap):(HbufC、HbufC8),數據存儲于堆上,大小在運行時確定,也就是是用來處理動態申請的描述符類。在C/C++中用過動態內存的都知道,動態內存是啥回事,這里堆類描述符用的時候,也是差不多,由于堆描述符沒有構造函數,所以只能聲明為指針類型,通過堆描述符類內靜態函數NewL方法申請內存,具體方法如下
HBufC* errorTitleCode = HBufC::NewLC(50);
HbufC* unUseCode = NULL;
· 指針類(Pointer):(TPtr、TPtrC、TPtr8、TPtrC8),本身不包含描述符數據,但是包含長度數據,而且還包含一個指向位于描述符之外數據的指針。
從以上分類可知,描述符有8位和16位寬度的區別,還有可修改和不可修改的區別,具體的區別我從內存的角度出發列表如下:
具體類型
|
類型(4b)
|
當前長度(28b)
|
最大長度(32b)
|
Buffer
|
TDesC8
|
|
Yes
|
無
|
無
|
TDesC
|
|
Yes
|
無
|
無
|
TDes8
|
|
Yes
|
Yes
|
無
|
TDes
|
|
Yes
|
Yes
|
無
|
TBufC8
|
0
|
Yes
|
無
|
ByteBuffer
|
TBufC
|
0
|
Yes
|
無
|
WordBuffer
|
TBuf8
|
3
|
Yes
|
Yes
|
ByteBuffer
|
TBuf
|
3
|
Yes
|
Yes
|
WordBuffer
|
TPtrC8
|
1
|
Yes
|
無
|
32位指針
|
TPtrC
|
1
|
Yes
|
無
|
32位指針
|
TPtr8
|
2
|
Yes
|
Yes
|
32位指針
|
TPtr
|
2
|
Yes
|
Yes
|
32位指針
|
HBufC8
|
0
|
Yes
|
無
|
ByteBuffer
|
HBufC
|
0
|
Yes
|
無
|
WordBuffer
|
注:
1、 表中空出的內容,我暫時還不知道具體的值是多少。其中的b是bit的意思;
2、 在實際操作中,定義的描述符長度和內存實際使用長度會有不一致問題,原因是描述符也是按4字節進行邊界對齊的。
由如表所示的內存關系可能顯得有點亂,如果能將每個類用UML類圖(包括詳盡的成員變量和成員函數,類的頭文件在e32des16.h和e32des8.h中)來表示就更直觀了,在Symbian官方網站有一張類簡圖,我先將此作為繼承關系的簡圖在這里作為演示用
三、描述符的使用
介紹到這里應該具體講每一個描述類的使用了,我發現有兩篇中文文檔整理的很好,在這里,我只做一些驗證性的介紹,讀者可以參閱我在文章后列出的兩篇文檔。由于要略過不能實例化的抽象類,且按照從簡單到復雜的過程來敘述:
1、 文字描述符常量
a、_LIT()可以生成個常量名,以便以后重復使用,例如
_LIT(KMyFile, "c:\System\Apps\MyApp\MyFile.jpg");
_LIT()宏的結果(就是上面的KMyFile)實際上是個文字描述符(literal descriptor)TLitC,它可以在任何使用TDesC&的地方使用。(但是TlitC已經不推薦使用了)。
b、_L()可以生成一個指向字符值的地址(TPtrC),它經常被用來傳遞字符串到函數中(包括描述符的構造函數和格式化函數);同理_L8()則可以生成一個指向二進制數據的地址(TPtrC8)舉例如下:
//常用的通知函數
NEikonEnvironment::MessageBox(_L("Error: init file not found!"));
//數字轉字符串
TBuf16<20> buf;//
TInt iNum = 20;
buf.Format( _L( "%d" ) , iNum );
2、 棧描述符
棧類描述符聲明時必須指定描述符的最大長度,否則無法聲明和定義,下面舉例
例1:構造
// 直接從字符串中構造
_LIT(Ktext, "TestText");
TBufC<10> Buf (Ktext);
// 或從字符串賦值
TBufC<10> Buf2;
Buf2 = Ktext;
// 從已有的對象中生成新的TBufC
TBufC<10> Buf3(Buf2);
TBufC<n>一般用來存儲文本數據,而TBufC8<n>則用來存儲二進制數據。盡管這里的對象表示數據是不能被修改的(因為有個后綴C代表了常量的意思),但仍然有兩種方式可以用來修改數據內容:這里的數據可以用賦值的方式替換掉;使用Des()函數構造出一個TPtr對象,這樣就可以用它來修改數據。
例2:修改數據
_LIT(Ktext , "Test Text");
_LIT(Ktext1 , "Test1 Text");
_LIT(KXtraText , "New:");
_LIT(NewText , "New1");
_LIT(NewText1 , "New2");
TBufC<10> Buf1 ( Ktext );//Buf1長度為9 內容 “Test Text”
TBufC<10> Buf2 ( Ktext1 );//Buf2長度為10 內容 “Test1 Text”
// 通過賦值的方式改變數據
Buf2 = Buf1; //Buf2長度變為9 內容 “Test Text”
//通過使用Des()生成指針改變TBufC的數據
TPtr Pointer = Buf1.Des();
// 刪除后四個字符
Pointer.Delete(Pointer.Length()-4, 4 ); //Buf1長度變為5 內容“Test ”
//但是內存應該沒變
// 增加新的數據
Pointer.Append(KXtraText);//Buf1長度為9 內容為“Test New:”
// 也可以使用下列方式改變數據
TBufC<10> Buf3(NewText);
Pointer.Copy(Buf3);//Buf1長度為4,內容為New1
// 或直接從字符串里獲得數據
Pointer.Copy(NewText1);//Buf1長度為4,內容為New2
以上介紹的是不可修改的棧描述符,而可修改的描述符就不用通過那么復雜的方法來實現修改,它直接可以用Copy、Delete等方法,但是無論可修改的還是不可修改的,一旦指定最大的數據長度后,最大長度就不能進行修改了。修改的只是數據內容,而數據內容修改的受限條件是不能超過聲明或定義時的最大長度。(個人以為從內存角度來說,不可修改類型的缺少最大長度,所以嚴格上來說為了減少錯誤,修改數據內容是不允許的)
3、 堆描述符
堆描述符雖然都是不可修改類型的,但是它仍然具有構造和修改,與棧描述符不同的是:首先對內存需要顯示釋放,其次是堆描述符沒有最大長度的限制,任何時候都可以用ReAlloc()函數重新申請分配。具體見示例:
//例1、構造
//有兩種方式來生成一個Heap Descriptor
//第一種方式用New(),NewL(),或NewLC()
//如下操作便可以構建一個存放數據的空間,空間為15,不過目前大小為0
HBufC * Buf = HBufC::NewL(15);
//第二種方式是采用Alloc(),AllocL()或AllcLC()來處理,
//不過這是已經存在的數據的管理方式。新的Heap Descriptor
//可以自動的根據這個內容來構造。
_LIT (KText , "Test Text");
TBufC<10> CBuf = KText;
HBufC * Buf1 = CBuf.AllocL();
CleanupStack::PushL(Buf1);
//例2、修改
//下面是通過賦值方式改變其數據的方法
_LIT ( KText1 , "Text1");
*Buf1 = KText1;
// 通過可修改指針來改變數據的方式
TPtr Pointer = Buf1->Des();
//添加數據
Pointer.Delete(Pointer.Length() - 2, 2);
//刪除數據
_LIT ( KNew, "New:");
Pointer.Append(KNew);
//例3、重新申請內存
Buf1 = Buf1->ReAllocL(KText().Length() + KNew().Length());
CleanupStack::PushL(Buf1);
//例4、釋放內存
//直接用delete
delete Buf;
Buf = NULL;
//如果在使用NewL、ReAllocL等異常函數后我們使用清除棧壓入的話
//那么我們也可以用清除棧來釋放內存
CleanupStack::PopAndDestroy();
Buf1 = NULL;
注:關于以上用清除棧的方式,個人只是猜測,因為對Symbian的異常處理三部曲,至今仍沒有很好的掌握,所以如果有什么誤用還望指點。
4、 指針描述符
其實關于指針描述符,我們在上面已經用過可修改的指針TPtr了,下面返璞歸真,從TPtrC的構造開始介紹使用
//例1、用TBuf和TBufC構造出TPtrC對象
_LIT(KText , "Test Code");
TBufC<10> Buf ( KText );
//或者為 TBuf<10> Buf ( KText );
// Creation of TPtrC using Constructor
TPtrC Ptr (Buf);
// Creation of TPtrC using Member Function
TPtrC Ptr1;
Ptr1.Set(Buf);
//例2、用TText*構造TPtrC
const TText* text = _S("Hello World\n");
TPtrC ptr(text);
// 或者
TPtrC Ptr2;
Ptr2.Set(text);
//如果要存儲TText的一部分數據,我們使用下列方法
TPtrC ptr4(text, 5);
//例3、從另一個TPtrC中構造TPtrC
const TText * text1 = _S("Hello World\n");
TPtrC Ptr3(text1);
// 從一個TPtrC中獲得另一個TPtrC
TPtrC p1(Ptr3);
// 或
TPtrC p2;
p2.Set(Ptr3);
以上是不可修改的TPtrC的構造,相對應的也有可修改的TPtr的構造,不過我們下面省略了用Set()函數的構造方法
//例1、通過TBufC,HBufC的Des()方法獲取
_LIT(KText, "Test Data");
TBufC<10> NBuf ( KText );
TPtr Pointer = NBuf.Des();
//例2、通過指定內存區域和大小來生成
const TText * Text = _S("Test Second");
TPtr Pointer1((TText*)Text, 11, 12);
//例3、 通過另一個TPtr對象來生成
TPtr Pointer2 ( Pointer );
對于可修改的TPtr雖然前面用過,但是我們在這里在簡單的添加兩個例子加深下印象,并且說明指針修改的始終是它指向的描述符:
//例1、改變已有TPtr數據的方式:賦值和Copy()方法
_LIT(KText, "Test Data");
_LIT(K1, "Text1");
_LIT(K2, "Text2");
TBufC<10> NBuf ( KText );//NBuf內容為“Test Data”
TPtr Pointer = NBuf.Des(); //Pointer指向NBuf的內容
Pointer = K1; // NBuf內容為“Text1”
Pointer.Copy(K2); // NBuf內容為“Text2”
//例2、直接通過修改長度改變數據內容
Pointer.SetLength(2); // NBuf內容為"Te" 注:實際內存的內容應該沒變
5、 抽象描述符
抽象描述符,沒有什么好說的,正如前面所說,只用在函數的形參中,通常要強調參數是不可修改的,就用const TDesC&表示,可修改的參數用TDesC&表示。
四、常用API函數羅列
下面再對描述符的幾個常用修改和不可修改API函數加以羅列
不可修改型:
//獲取屬性類
Length(),Size()
//查找、比較類
Compare(),Locate(),LocateReverse (),Find(),Match()
//取描述符子串指針類
Left(),Right(),Mid()
可修改型:
//增加、插入、刪除類
Insert(),Delete(),Append(),Replace(), Trim()
//賦值類
Zero(),Copy(),Num(),Format()
本文涉及的兩篇文檔
Nokia官方培訓(Symbian4300)筆記(六)—Descriptors(該文鏈接google里面找)
中文 Descriptors的使用
http://wiki.forum.nokia.com/index.php/%E4%B8%AD%E6%96%87_Descriptors%E7%9A%84%E4%BD%BF%E7%94%A8#TPtr.E7.9A.84.E4.BD.BF.E7.94.A8
后記:本想把涉及描述符的轉換也整理在一篇文章中的,但是后來發現整理完概念和使用已經消耗了我一天多時間了,晚上部門要去喝茶,為此暫時到這里,明天周末在整理轉換問題。由衷感謝單位給我那么多時間在工作中學習,想想以前第一份工作時,在單位看書都要被老板說的歷史,發覺自己現在真的蠻幸運的。
本文由于圖片不能顯示同樣給出word版本地址http://www.shnenglu.com/Files/franksunny/描述符Descriptors(上).rar
posted on 2007-10-19 17:23
frank.sunny 閱讀(3618)
評論(6) 編輯 收藏 引用 所屬分類:
symbian 開發