Ø TReader قم أولاً بإلقاء نظرة على ملف مشروع دلفي، وستجد بضعة أسطر من التعليمات البرمجية مثل هذا: begin application.Initialize; Application.CreateForm(TForm1, Form1;Run;end). . دعونا نتحدث بإيجاز عن معنى هذه الأسطر من التعليمات البرمجية: يقوم Application.Initialize ببعض أعمال التهيئة الضرورية للتطبيق قيد التشغيل، ويقوم Application.CreateForm(TForm1, Form1) بإنشاء النماذج اللازمة، ويبدأ برنامج Application.Run في التشغيل ويدخل الرسالة دورة. أكثر ما يهمنا الآن هو جملة إنشاء النموذج. كيف يتم إنشاء النماذج والمكونات الموجودة في النموذج؟ كما ذكرنا من قبل: يتم تضمين كافة المكونات الموجودة في النموذج، بما في ذلك خصائص النموذج نفسه، في ملف DFM. عندما تقوم دلفي بتجميع البرنامج، فإنها تستخدم تعليمات التجميع {$R *.dfm} لتجميع معلومات ملف DFM. في الملف القابل للتنفيذ. لذلك، يمكن أن نستنتج أنه عند إنشاء نموذج، تحتاج إلى قراءة معلومات سوق دبي المالي، ما الذي يجب عليك استخدامه لقراءته؟ بالطبع هو TReader! ومن خلال تتبع البرنامج خطوة بخطوة، نجد أن البرنامج يستدعي طريقة ReadRootComponent الخاصة بـ TReader أثناء عملية إنشاء النموذج. تتمثل وظيفة هذه الطريقة في قراءة المكون الجذر وجميع المكونات التي يمتلكها. دعونا نلقي نظرة على تنفيذ هذه الطريقة: function TReader.ReadRootComponent(Root: TComponent): TComponent;...begin ReadSignature; = nil; GlobalNameSpace.BeginWrite; // التحميل من الدفق يضيف إلى مساحة الاسم حاول تجربة ReadPRefix (Flags, I ); إذا كان الجذر = لا شيء، فابدأ بالنتيجة:= TComponentClass(FindClass(ReadStr)).Create(nil); Result.Name := ReadStr; end else begin Result := Root; { Ignore class name } إذا كان csDesigning في Result.ComponentState ثم يبدأ ReadStr في Include(Result.FComponentState, csLoading); FindUniqueName(ReadStr); end; TClassFinder.Create(TPersistentClass(Result.ClassType), True); حاول FLookupRoot := G := GlobalLoaded; إذا كان G <> nil ثم FLoaded := G else FLoaded := TList.Create; ) < 0 ثم FLoaded.Add(FRoot); Include(FRoot.FComponentState, csLoading); Include(FRoot.FComponentState, csReading); FRoot.ReadState(Self); قم بإجراء TComponent(FLoaded[I]).Loaded; أخيرًا إذا كان G = nil ثم FLoaded.Free; nil; end; أخيرًا FFinder.Free; end;... أخيرًا GlobalNameSpace.EndWrite; end; يقوم ReadRootComponent أولاً باستدعاء ReadSignature لقراءة علامة كائن Filer ('TPF0'). يؤدي اكتشاف العلامات قبل تحميل الكائنات إلى منع القراءة غير المقصودة للبيانات غير الصالحة أو القديمة. ألق نظرة أخرى على الجملة ReadPrefix(Flags, I). وظيفة طريقة ReadPrefix مشابهة جدًا لوظيفة ReadSignature، باستثناء أنها تقرأ العلامة (PreFix) أمام المكون في الدفق. عندما يكتب كائن الكتابة مكونًا في الدفق، فإنه يكتب مسبقًا قيمتين أمام المكون، تشير القيمة الأولى إلى ما إذا كان المكون عبارة عن نموذج موروث من نموذج سلف وما إذا كان موضعه في النموذج هو علامة مهمة. تحدد القيمة الثانية الترتيب الذي تم به إنشاء نموذج الأصل. بعد ذلك، إذا كانت المعلمة الجذر صفرًا، فقم بإنشاء مكون جديد باسم الفئة الذي تمت قراءته بواسطة ReadStr، واقرأ سمة اسم المكون من الدفق؛ وإلا، فتجاهل اسم الفئة وحدد تفرد سمة الاسم. FRoot.ReadState(Self); هذه جملة حرجة للغاية. يقرأ أسلوب ReadState خصائص المكون الجذر والمكونات المملوكة له. على الرغم من أن طريقة ReadState هذه هي إحدى طرق TComponent، إلا أن المزيد من التتبع يمكن أن يكشف أنه في النهاية يحدد موقع طريقة ReadDataInner الخاصة بـ TReader. تنفيذ هذه الطريقة كما يلي: الإجراء TReader.ReadDataInner(Instance: TComponent);var OldParent, OldOwner: TComponent. ;ابدأ بينما لا تقوم EndOfList بـ ReadProperty(Instance ReadListEnd); Instance.GetChildParent; حاول المالك := Instance.GetChildOwner; يوجد هذا السطر من التعليمات البرمجية: بينما لا EndOfList do ReadProperty(Instance); يُستخدم هذا لقراءة خصائص المكون الجذر، كما ذكرنا من قبل، هناك خصائص منشورة للمكون نفسه وخصائص غير منشورة، مثل TTimer's Left وTop. بالنسبة لهاتين الخاصيتين المختلفتين، يجب أن يكون هناك طريقتان مختلفتان للقراءة من أجل التحقق من هذه الفكرة، دعونا نلقي نظرة على تنفيذ طريقة ReadProperty. الإجراء TReader.ReadProperty(AInstance: TPersistent);...begin... PropInfo := GetPropInfo(Instance.ClassInfo, FPropName); إذا PropInfo <> nil ثم ReadPropValue(Instance, PropInfo) else begin { لا يمكن التعافي بشكل موثوق من خطأ في خاصية محددة } FCanHandleExcepts := False; Instance.DefineProperties(Self); FCanHandleExcepts := True; إذا كان FPropName <> '' ثم PropertyError(FPropName); القراءة من اسم سمة الملف. PropInfo := GetPropInfo(Instance.ClassInfo, FPropName); هذا الرمز هو الحصول على معلومات السمة المنشورة FPropName. كما ترون من الكود التالي، إذا لم تكن معلومات السمة فارغة، فستتم قراءة قيمة السمة من خلال طريقة ReadPropValue، وتقرأ طريقة ReadPropValue قيمة السمة من خلال وظيفة RTTI، والتي لن يتم وصفها بالتفصيل هنا. إذا كانت معلومات السمة فارغة، فهذا يعني أن السمة FPropName غير منشورة، ويجب قراءتها من خلال آلية أخرى. هذه هي طريقة DefineProperties المذكورة سابقًا، كما يلي: Instance.DefineProperties(Self); تستدعي هذه الطريقة بالفعل طريقة DefineProperty الخاصة بـ TReader: الإجراء TReader.DefineProperty(const Name: string; ReadData: TReaderProc; WriteData: TWriterProc; HasData: Boolean) ؛ ابدأ إذا كان SameText(Name, FPropName) وAssigned(ReadData) ثم begin ReadData(Self); FPropName := ''; end;end; يقارن أولاً ما إذا كان اسم سمة القراءة هو نفس اسم السمة المحددة مسبقًا، وإذا كانت طريقة القراءة ReadData ليست فارغة يتم استدعاؤه لقراءة قيمة السمة. حسنًا، تمت قراءة المكون الجذر، والخطوة التالية هي قراءة المكونات المملوكة للمكون الجذر. دعونا نلقي نظرة على الطريقة: الإجراء TReader.ReadDataInner(Instance: TComponent); يوجد رمز مثل هذا بعد هذه الطريقة: بينما لا يقوم EndOfList بقراءة المكونات الفرعية (nil); آلية قراءة المكونات الفرعية هي نفس قراءة المكون الجذري المقدم أعلاه، وهو اجتياز عميق للشجرة. حتى الآن، تم تقديم آلية قراءة المكونات. دعونا نلقي نظرة على آلية كتابة المكونات. عندما نضيف مكونًا إلى النموذج، سيتم حفظ خصائصه ذات الصلة في ملف DFM. يتم إكمال هذه العملية بواسطة TWriter. Ø TWriter كائن TWriter هو كائن Filer قابل للإنشاء يكتب البيانات إلى الدفق. يرث كائن TWriter مباشرة من TFiler، بالإضافة إلى تجاوز الأساليب الموروثة من TFiler، فإنه يضيف أيضًا عددًا كبيرًا من الأساليب لكتابة أنواع البيانات المختلفة (مثل Integer وString وComponent وما إلى ذلك). يوفر كائن TWriter العديد من الأساليب لكتابة أنواع مختلفة من البيانات في الدفق. يكتب كائن TWrite البيانات في الدفق بتنسيقات مختلفة بناءً على بيانات مختلفة. لذلك، لإتقان أساليب التنفيذ والتطبيق لكائن TWriter، يجب عليك فهم التنسيق الذي يقوم كائن Writer بتخزين البيانات به. أول شيء يجب ملاحظته هو أن دفق كل كائن Filer يحتوي على علامة كائن Filer. تشغل هذه العلامة أربعة بايتات وقيمتها هي "TPF0". يصل كائن Filer إلى العلامة الخاصة بأساليب WriteSignature وReadSignature. تُستخدم هذه العلامة بشكل أساسي لتوجيه عملية القراءة عندما يقرأ كائن القارئ البيانات (المكونات، وما إلى ذلك). ثانيًا، يجب أن يترك كائن الكاتب علامة بايت قبل تخزين البيانات للإشارة إلى نوع البيانات التي سيتم تخزينها لاحقًا. هذه البايت هي قيمة من النوع TValueType. TValueType هو نوع تعداد يشغل مساحة بايت واحدة ويتم تعريفه على النحو التالي: TValueType = (VaNull, VaList, VaInt8, VaInt16, VaInt32, VaEntened, VaString, VaIdent, VaFalse, VaTrue, VaBinary, VaSet, VaLString, VaNil, VaCollection); لذلك، بالنسبة لكل طريقة لكتابة بيانات كائن الكاتب، من حيث التنفيذ، يجب كتابة بت العلامة أولاً ثم البيانات المقابلة، ويجب على كل طريقة لقراءة البيانات لكائن القارئ قراءة بت العلامة أولاً للحكم عليها يطابق بيانات القراءة، وإلا فسيتم إنشاء حدث استثناء يشير إلى أن بيانات القراءة غير صالحة. علامة VaList لها غرض خاص، فهي تستخدم لتحديد أنه ستكون هناك سلسلة من العناصر من نفس النوع لاحقًا، والعلامة التي تحدد نهاية العناصر المتتالية هي VaNull. لذلك، عندما يكتب كائن الكاتب عدة عناصر متطابقة متتالية، فإنه يكتب أولاً علامة VaList باستخدام WriteListBegin، ثم يكتب علامة VaNull بعد كتابة عناصر البيانات، ويبدأ بـ ReadListBegin، وينتهي بـ ReadListEnd، ويستخدم وظيفة EndofList في المنتصف لتحديد ما إذا كانت هناك علامة VaNull. دعونا نلقي نظرة على طريقة مهمة جدًا لـ TWriter: WriteData: الإجراء TWriter.WriteData(Instance: TComponent);... begin... WritePrefix(Flags, FChildPos); .ClassInfo)) .UnitName + '.' + Instance.ClassName) else WriteStr(Instance.ClassName); WriteStr(Instance.Name); PropertiesPosition := Position; if (FAncestorList <> nil) و (FAncestorPos < FAncestorList.Count) ثم ابدأ إذا كان Ancestor <> nil ثم Inc(FAncestorPos Inc(FChildPos end); ); WriteListEnd; ...end; من طريقة WriteData يمكننا رؤية نظرة عامة حول إنشاء معلومات ملف DFM. قم أولاً بكتابة العلامة (PreFix) أمام المكون، ثم اكتب اسم الفئة واسم المثيل. مباشرة بعد هذا البيان: WriteProperties(Instance); يستخدم لكتابة خصائص المكون. كما ذكرنا سابقًا، يوجد في ملف DFM سمات منشورة وسمات غير منشورة، ويجب أن تكون طرق الكتابة لهاتين السمتين مختلفة. دعونا نلقي نظرة على تنفيذ WriteProperties: الإجراء TWriter.WriteProperties(Instance: TPersistent);...begin Count := GetTypeData(Instance.ClassInfo)^.PropCount; إذا كان Count > 0، فابدأ GetMem(PropList, Count * SizeOf(Pointer )); حاول GetPropInfos(Instance.ClassInfo, PropList); PropInfo := PropList^[I]; if PropInfo = nil ثم Break; if IsStoredProp(Instance, PropInfo) ثم WriteProperty(Instance, PropInfo end; Instance.DefineProperties(Self);end; يرجى الاطلاع على الكود التالي: if IsStoredProp(Instance, PropInfo) ثم WriteProperty(Instance, PropInfo); تستخدم الوظيفة IsStoredProp مؤهل التخزين لتحديد ما إذا كانت الخاصية بحاجة إلى حفظها، فاتصل بـ WriteProperty لحفظ الخاصية، ويتم تنفيذ WriteProperty من خلال سلسلة من RTTI. وظائف. بعد حفظ الخصائص المنشورة، يجب حفظ الخصائص غير المنشورة من خلال هذا الكود: Instance.DefineProperties(Self); تم ذكر تنفيذ DefineProperties من قبل . حسنًا، لا يزال هناك سؤال حتى الآن: كيف يتم حفظ المكونات الفرعية التي يملكها المكون الجذر؟ دعونا نلقي نظرة على طريقة WriteData (تم ذكر هذه الطريقة سابقًا): الإجراء TWriter.WriteData(Instance: TComponent);...ابدأ... إذا لم يكن IgnoreChildren ثم حاول إذا (FAncestor <> nil) و(FAncestor هو TComponent) ثم ابدأ إذا (FAncestor هو TComponent) و(csInline في TComponent(FAncestor).ComponentState) ثم FRootAncestor := TComponent(FAncestorList := TList.Create; حر؛نهاية؛نهاية؛ تسمح سمة IgnoreChildren لكائن الكاتب بتخزين مكون دون تخزين المكونات الفرعية المملوكة للمكون. إذا كانت الخاصية IgnoreChildren صحيحة، فسيقوم كائن الكاتب بتخزين المكون دون تخزين المكونات التابعة التي يمتلكها. وبخلاف ذلك، يجب تخزين المكون الفرعي. Instance.GetChildren(WriteComponent, FRoot); هذه هي الجملة الأكثر أهمية عند كتابة مكون فرعي، فهي تستخدم أسلوب WriteComponent كوظيفة رد اتصال وتتبع مبدأ اجتياز العمق أولاً للشجرة إذا كان المكون الجذر FRoot يحتوي على مكون فرعي استخدم WriteComponent لحفظه. بهذه الطريقة، ما نراه في ملف DFM هو بنية مكونة تشبه الشجرة.