Ø TReader Pertama lihat file proyek Delphi, dan Anda akan menemukan beberapa baris kode seperti ini: start application.Initialize; Application.CreateForm(TForm1, Form1 Application.Run;end). . Mari kita bahas secara singkat arti dari baris kode ini: Application.Initialize melakukan beberapa pekerjaan inisialisasi yang diperlukan untuk aplikasi yang sedang berjalan, Application.CreateForm(TForm1, Form1) membuat formulir yang diperlukan, dan program Application.Run mulai berjalan dan memasukkan pesan siklus. Yang paling kami khawatirkan saat ini adalah kalimat membuat formulir. Bagaimana formulir dan komponen pada formulir dibuat? Seperti disebutkan sebelumnya: semua komponen dalam formulir, termasuk properti formulir itu sendiri, disertakan dalam file DFM. Ketika Delphi mengkompilasi program, Delphi menggunakan instruksi kompilasi {$R *.dfm} untuk mengkompilasi informasi file DFM. ke dalam file yang dapat dieksekusi. Oleh karena itu, dapat disimpulkan bahwa saat membuat formulir, Anda perlu membaca informasi DFM. Apa yang harus Anda gunakan untuk membacanya? Dengan menelusuri program langkah demi langkah, kita dapat menemukan bahwa program tersebut memanggil metode ReadRootComponent dari TReader selama proses pembuatan formulir. Fungsi dari metode ini adalah untuk membaca komponen root dan seluruh komponen yang dimilikinya. Mari kita lihat penerapan metode ini: function TReader.ReadRootComponent(Root: TComponent): TComponent;...begin ReadSignature; Hasil := GlobalNameSpace.BeginWrite; (Bendera, I ); jika Root = nil maka mulai Hasil := TComponentClass(FindClass(ReadStr)).Create(nil); ReadStr; akhiri lagi mulai Hasil := Root; ReadStr; { Abaikan nama kelas } jika csDesigning di Result.ComponentState lalu ReadStr mulai Sertakan(Result.FComponentState, csLoading); TemukanNamaUnik(ReadStr); akhir; FRoot := Hasil; TClassFinder.Create(TPersistentClass(Result.ClassType), True); coba FLookupRoot := Hasil; G := GlobalLoaded; jika G <> nil maka FLoaded := G else FLoaded := TList.Create; ) < 0 lalu FLoaded.Add(FRoot); Sertakan(FRoot.FComponentState, csLoading); Sertakan(FRoot.FComponentState, csReading); FRoot.ReadState(Self); Exclude(FRoot.FComponentState, csReading); lakukan TComponent(FLloaded[I]).Loaded; akhirnya jika G = nil maka FLoaded.Free; nil; end; akhirnya FFinder.Free; end; ... akhirnya GlobalNameSpace.EndWrite; end;end; pertama-tama memanggil ReadSignature untuk membaca tag objek Filer ('TPF0'). Mendeteksi tag sebelum memuat objek mencegah pembacaan data yang tidak valid atau ketinggalan jaman secara tidak sengaja. Perhatikan lagi kalimat ReadPrefix(Flags, I). Fungsi metode ReadPrefix sangat mirip dengan ReadSignature, hanya saja ia membaca flag (PreFix) di depan komponen dalam aliran. Ketika objek Write menulis komponen ke dalam aliran, ia menulis dua nilai di depan komponen terlebih dahulu. Nilai pertama menunjukkan apakah komponen tersebut merupakan formulir yang diwarisi dari formulir leluhur dan apakah posisinya dalam formulir adalah tanda Penting ; nilai kedua menentukan urutan pembuatan bentuk leluhur. Kemudian, jika parameter Root adalah nihil, buat komponen baru dengan nama kelas yang dibaca oleh ReadStr, dan baca atribut Nama komponen dari aliran; jika tidak, abaikan nama kelas dan tentukan keunikan atribut Nama. FRoot.ReadState(Self); Ini adalah kalimat yang sangat penting. Metode ReadState membaca properti komponen root dan komponen yang dimilikinya. Meskipun metode ReadState ini adalah metode TComponent, pelacakan lebih lanjut dapat mengungkapkan bahwa metode tersebut pada akhirnya menemukan metode ReadDataInner dari TReader. Implementasi metode ini adalah sebagai berikut: procedure TReader.ReadDataInner(Instance: TComponent);var OldParent, OldOwner: TComponent ;mulai saat bukan EndOfList lakukan ReadProperty(Instance); Instance.GetChildParent; coba Pemilik := Instance.GetChildOwner; jika tidak Ditugaskan(Pemilik) maka Pemilik := Pemilik Lama := Pemilik Lama; Ada baris kode ini: sementara bukan EndOfList lakukan ReadProperty(Instance); Ini digunakan untuk membaca properti komponen root. Seperti disebutkan sebelumnya, ada properti yang dipublikasikan dari komponen itu sendiri dan properti yang tidak dipublikasikan, seperti Left dan Top TTimer. Untuk dua properti berbeda ini, harus ada dua metode pembacaan yang berbeda. Untuk memverifikasi gagasan ini, mari kita lihat implementasi metode ReadProperty. procedure TReader.ReadProperty(AIinstance: TPersistent);…begin… PropInfo := GetPropInfo(Instance.ClassInfo, FPropName); if PropInfo <> nil maka ReadPropValue(Instance, PropInfo) else Begin { Tidak dapat memulihkan kesalahan pada properti yang ditentukan secara andal } FCanHandleExcepts := Salah; Instance.DefineProperties(Self); FCanHandleExcepts := True; if FPropName <> '' maka PropertyError(FPropName); membaca dari nama atribut file. PropInfo := GetPropInfo(Instance.ClassInfo, FPropName); Kode ini untuk mendapatkan informasi atribut FPropName yang dipublikasikan. Seperti yang Anda lihat dari kode berikut, jika informasi atribut tidak kosong, nilai atribut dibaca melalui metode ReadPropValue, dan metode ReadPropValue membaca nilai atribut melalui fungsi RTTI, yang tidak akan dijelaskan secara rinci di sini. Jika informasi atribut kosong, berarti atribut FPropName tidak dipublikasikan, dan harus dibaca melalui mekanisme lain. Ini adalah metode DefineProperties yang disebutkan sebelumnya, sebagai berikut: Instance.DefineProperties(Self); Metode ini sebenarnya memanggil metode DefineProperty dari TReader: procedure TReader.DefineProperty(const Name: string; ReadData: TReaderProc; WriteData: TWriterProc; HasData: Boolean) ;mulai jika SameText(Nama, FPropName) dan Ditugaskan(ReadData) lalu mulai ReadData(Self); FPropName := ''; end;end; Pertama-tama membandingkan apakah nama atribut baca sama dengan nama atribut preset. Jika keduanya sama dan metode baca ReadData tidak kosong, metode ReadData dipanggil untuk membaca nilai atribut. Oke, komponen root sudah terbaca, dan langkah selanjutnya adalah membaca komponen yang dimiliki oleh komponen root tersebut. Mari kita lihat metodenya: procedure TReader.ReadDataInner(Instance: TComponent); Ada kode seperti ini setelah metode ini: while not EndOfList do ReadComponent(nil); Mekanisme pembacaan subkomponen sama dengan pembacaan komponen akar yang diperkenalkan di atas, yaitu penelusuran mendalam pada pohon. Sejauh ini, mekanisme pembacaan komponen telah diperkenalkan. Mari kita lihat mekanisme penulisan komponen. Saat kita menambahkan komponen ke formulir, properti terkaitnya akan disimpan dalam file DFM. Proses ini diselesaikan oleh TWriter. Ø TWriter Objek TWriter adalah objek Filer instan yang menulis data ke aliran. Objek TWriter mewarisi langsung dari TFiler. Selain mengganti metode yang diwarisi dari TFiler, objek ini juga menambahkan sejumlah besar metode untuk menulis berbagai tipe data (seperti Integer, String, Komponen, dll.). Objek TWriter menyediakan banyak metode untuk menulis berbagai tipe data ke dalam aliran. Objek TWrite menulis data ke dalam aliran dalam format berbeda berdasarkan data berbeda. Oleh karena itu, untuk menguasai implementasi dan metode penerapan objek TWriter, Anda harus memahami format penyimpanan data objek Writer. Hal pertama yang perlu diperhatikan adalah aliran setiap objek Filer berisi tag objek Filer. Tag ini menempati empat byte dan nilainya adalah "TPF0". Objek Filer mengakses tag untuk metode WriteSignature dan ReadSignature. Tag ini terutama digunakan untuk memandu operasi pembacaan ketika objek Reader membaca data (komponen, dll.). Kedua, objek Writer harus meninggalkan tanda byte sebelum menyimpan data untuk menunjukkan tipe data apa yang disimpan nanti. Byte ini adalah nilai bertipe TValueType. TValueType adalah tipe enumerasi yang menempati ruang satu byte dan didefinisikan sebagai berikut: TValueType = (VaNull, VaList, VaInt8, VaInt16, VaInt32, VaEnended, VaString, VaIdent, VaFalse, VaTrue, VaBinary, VaSet, VaLString, VaNil, VaCollection); Oleh karena itu, untuk setiap metode penulisan data dari objek Writer, dalam hal implementasi, bit flag harus ditulis terlebih dahulu dan kemudian data yang sesuai; dan setiap metode pembacaan data dari objek Reader harus terlebih dahulu membaca bit flag untuk penilaian cocok dengan data yang dibaca, jika tidak, peristiwa pengecualian yang menunjukkan bahwa data yang dibaca tidak valid akan dihasilkan. Flag VaList memiliki tujuan khusus. Ini digunakan untuk mengidentifikasi bahwa nantinya akan ada serangkaian item dengan tipe yang sama, dan flag yang mengidentifikasi akhir dari item yang berurutan adalah VaNull. Oleh karena itu, ketika objek Writer menulis beberapa item identik berturut-turut, pertama-tama ia menulis tanda VaList dengan WriteListBegin, dan kemudian menulis tanda VaNull setelah menulis item data, saat membaca data ini, ia dimulai dengan ReadListBegin, diakhiri dengan ReadListEnd, dan menggunakan Fungsi EndofList di tengah. Tentukan apakah ada flag VaNull. Mari kita lihat metode yang sangat penting dari TWriter: WriteData: procedure TWriter.WriteData(Instance: TComponent);... mulai... WritePrefix(Flags, FChildPos); .ClassInfo)) .UnitName + '.' + Instance.ClassName) else WriteStr(Instance.ClassName); WriteStr(Instance.Name); PropertiesPosition := Posisi; jika (FAncestorList <> nil) dan (FAncestorPos < FAncestorList.Count) maka mulai jika Leluhur <> nihil lalu Inc(FAncestorPos); ); WriteListEnd; ...end; Dari metode WriteData kita dapat melihat gambaran umum pembuatan informasi file DFM. Pertama tulis flag (PreFix) di depan komponen, lalu tulis nama kelas dan nama instance. Segera setelah pernyataan ini: WriteProperties(Instance); Ini digunakan untuk menulis properti komponen. Seperti disebutkan sebelumnya, dalam file DFM, terdapat atribut yang dipublikasikan dan atribut yang tidak dipublikasikan. Cara penulisan kedua atribut ini harus berbeda. Mari kita lihat implementasi WriteProperties: procedure TWriter.WriteProperties(Instance: TPersistent);...begin Count := GetTypeData(Instance.ClassInfo)^.PropCount; jika Count > 0 maka mulai GetMem(PropList, Count * SizeOf(Pointer )); coba GetPropInfos(Instance.ClassInfo, PropList); untuk I := 0 hingga Hitung - 1 dimulai PropInfo := PropList^[I]; jika PropInfo = nil maka Break; jika IsStoredProp(Instance, PropInfo) maka WriteProperty(Instance, PropInfo end; Instance.DefineProperties(Self);end; Silakan lihat kode berikut: if IsStoredProp(Instance, PropInfo) lalu WriteProperty(Instance, PropInfo); Fungsi IsStoredProp menggunakan qualifier penyimpanan untuk menentukan apakah properti perlu disimpan. Jika perlu disimpan, panggil WriteProperty untuk menyimpan properti, dan WriteProperty diimplementasikan melalui serangkaian RTTI. fungsi. Setelah properti yang Diterbitkan disimpan, properti yang tidak dipublikasikan harus disimpan. Hal ini dilakukan melalui kode ini: Instance.DefineProperties(Self); Implementasi DefineProperties telah disebutkan sebelumnya. Properti Kiri dan Atas TTimer disimpan melaluinya . Oke, sejauh ini masih ada pertanyaan: Bagaimana cara menyimpan subkomponen yang dimiliki oleh komponen root? Mari kita lihat metode WriteData (metode ini telah disebutkan sebelumnya): procedure TWriter.WriteData(Instance: TComponent);...begin... jika bukan IgnoreChildren maka coba if (FAncestor <> nil) dan (FAncestor is TComponent) lalu mulai jika (FAncestor adalah TComponent) dan (csInline di TComponent(FAncestor).ComponentState) lalu FRootAncestor := TComponent(FAncestor); FAncestorList := TList.Create; TComponent(FAncestor).GetChildren(AddAncestor, FRootAncestor); jika csInline di Instance.ComponentState lalu FRoot := Instance; Gratis; akhir;akhir; Atribut IgnoreChildren memungkinkan objek Writer untuk menyimpan komponen tanpa menyimpan komponen anak yang dimiliki oleh komponen tersebut. Jika properti IgnoreChildren adalah True, objek Writer menyimpan komponen tanpa menyimpan komponen anak yang dimilikinya. Jika tidak, subkomponen harus disimpan. Instance.GetChildren(WriteComponent, FRoot); Ini adalah kalimat paling kritis saat menulis subkomponen. Kalimat ini menggunakan metode WriteComponent sebagai fungsi panggilan balik dan mengikuti prinsip traversal depth-first pada pohon , gunakan WriteComponent untuk menyimpannya. Dengan cara ini, apa yang kita lihat di file DFM adalah struktur komponen seperti pohon.