Title: Delphi object model study notes Keywords: Delphi object model Author: dREAMtHEATER Difficulty: Normal [] medium [x] High [] http: //dREAMtHEATER.yeah.netE-Mail: NoteXPad@163.com Completion Date: 2004 August 21
Summary Borland Object Pascal Object Model (now is now named Delphi Language) The same foundation services are provided, such as object creation services, object release services, object identification services, object information services, Some additional services are available at the compiler and the VCL Framework level, such as an object message assignment service.
The foreword first said, the concept involved in the Delphi object model, so in this note, I can't point all the knowledge points, just draw a line of convenience. It can be said that this part is not very easy to understand. It is recommended that you look at the VCL source. It is really a Jinshan. You have a golden gold, you will have an unexpected harvest every time you. In addition, some concepts are afraid to see the source code is not to understand. At this time, you can take a look at the result of the anti-assessment, usually see the compiler has done a lot behind the scenes. With this kind of drilling spirit, I think there is nothing to solve.
The source of all things in Delphi is TOBJECT, whether your custom class indicates the inherited parent class, must all be a top grandson of TOBJECT, and all features defined by TOBJECT [3]. Since most of the object base services have been provided in TOBJECT, inheritance classes naturally have these object services, it is strongly recommended that every child who learns Delphi must carefully study the source code of TOBJECT. An object's life cycle begins at that moment from it being created. Usually we have created an object with a statement like TMYOBJECT.CREATE, then do you know what happened behind this line of code? It can be said that there are countless things, and the object has lived. First, the object creation service Every way to declare the constructor is a constructor, no matter where you don't have create as a method name, it is recommended to use create as a method name; thus, the compiler is based on the constructor's indicator (Directive) to generate Constructing code, rather than based on CREATE method, quite some people misunderstand the implementation process of constructor. The generation of a class instance requires three steps of object memory allocation, memory initialization, and set objects to perform frameworks. The compiler first calls System._ClassCreate for object memory allocation, memory initialization. SYSTEM._CLASSCREATE calls the ineffective method of the TOBJECT class NewInStance to establish an instance space of the object, inheritable class usually does not need to overload TOBJECT.NEWINSTANCE unless you use your own memory manager, the default is to call Tobject.newInstance. The TOBJECT.NEWINSTANCE method will be based on the object instance size (TOBJECT.INSTANCESIZE) initialized in the class information data, and the system default MemoryManager.getMem process is allocated in the heap (HEAP), then call Tobject.initInstance The method will initialize the space allocated. The InitInstance method first initials the heads of the object space to the VMT of the object class, and then clear the remaining space. If the interface is also designed in the class, it also initializes the interface table. When the object instance is assigned and initialized in the memory, the execution frame is started. The so-called setting execution frame is the code that is really written in the CREATE method. Set the rule of the execution frame to set the framework of the base class, then set the inheritance class, usually in the inherited keyword. After the above work is finished, the compiler also wants to call System._AFTERCONSTRUCTION to make you have the last opportunity to process some transactions. System._afterconstruction is called virtual methodAfterconstruction. On the anfterconstruction in TOBJECT is just a Place Holder, you rarely need to overload this method, and overload this method is usually only compatible with the C Builder object model. Finally, the compiler returns the address pointer of the object instance data. It should be noted that the constructor is a mixture of objects and class methods, which can be called with object references or class references [4]. Class reference mode The object is created in accordance with the above steps, and the object reference mode will only perform the step of the Setup Perform Framework, which is an instance of an object reference.
Related TObject Method: TObject = class // ... constructor Create; class function InitInstance (Instance: Pointer): TObject; class function InstanceSize: Longint; procedure AfterConstruction; virtual; class function NewInstance: TObject; virtual; // ... End; function _classcreate (aclass: tclass; alloc: boolean): TOBJECT; FUNCTION _AFTERCONSTRUction (Instance: Tobject): TOBJECT
// Don't forget to see this special example of this object, the code is located in Forms Unit Tapplication = Class (TComponent) Public Procedure Createform (VAR Reference); 2. The object identification service object identification service is to let you Determine the type of object currently used and inheritance architecture [1], although it is sometimes used in your code, you have used these methods indirectly, such as often used class operator IS, it It is true to call Tobject.inheritsFrom. Related TObject Method: TObject = class // ... class function ClassName: ShortString; class function ClassNameIs (const Name: string): Boolean; class function ClassParent: TClass; class function InheritsFrom (AClass: TClass): Boolean; class function InstanceSize : Longint; // ... end; 3 Object system information such as the method and the method of the object [1]. The content of this involve various knowledge of RTTI, interface, etc., you need to open a document, please keep paying attention to my homepage related content. Special note: Li Wei "Inside VCL" is incorrect when explaining the methodname method. After verification, the conclusion is that the method of declaring the Publish section of the class can get the name of the method by calling TOBJECT.METHODNAME. Instead of only the Published method of the class inherited by Tcomponent.
Related TObject Method: TObject = class // ... function ClassType: TClass; class function ClassInfo: Pointer; class function MethodAddress (const Name: ShortString): Pointer; class function MethodName (Address: Pointer): ShortString; function FieldAddress (const name: ShortString): Pointer; function GetInterface (const IID: TGUID; out Obj): Boolean; class function GetInterfaceEntry (const IID: TGUID): PInterfaceEntry; class function GetInterfaceTable: PInterfaceTable; function SafeCallException (ExceptObject: TObject; ExceptAddr: Pointer) : HRESULT; Virtual; // ... END; 4, Object Release Services Any way to declare DESTRUCTOR is a destructor, no matter where you don't have DESTROY as a method name, it is recommended to use DESTROY as a method name; there is a little bit To remember: Since DESTROY is declared in TOBJECT as a false method, if you also declare the destructor, don't forget to use the Override tag. Regarding why DESTROY is a virtual method, Li Wei explains too bad in "Inside VCL", and uses a number of sheets to analyze this problem, but never talks about the idea. In fact, there is only one reason: The destructor is often called, and the type of statement reference may be different from the actual category of the object [4]. Object release service is actually an inverse process for object creation services, which can be considered that the object release service is the resource allocated during the creation process. When the compiler encounters Destructor keywords, I usually encode this: first call System._beforDestruction, and System._BeForeDestruction, then call the virtual method BeforeDestruction, in the TOBJECT, beforedestruction is just a PlaceStruction, you rarely need to overload this method, overload This method is usually only compatible with the C Builder object model. After that, the compiler calls you really written in Destroy. If you are currently writing a class in the inheritance chain, don't forget to call the parent class's destructor's destructor with the resource allocated by inherited, but The rule is to release the resources of the current class, then call the parent class, this and the order of setting an object in the object creation service is just the opposite. After all of the resources allocated in all classes in the current class and inheritance chain, the last execution is to release the memory space occupied by the object itself and some special data types. The compiler calls System._ClassDestroy to complete this work. System._classdestroy then calls virtual method FreeInstance, inherited class usually does not need to overload TOBJECT.FREEINSTANCE unless you use your own memory manager, the default is to call Tobject.FreeInstance.
TOBJECT.FREEINSTANCE The TOBJECT.CLEANUPINSTANCE completes the release of strings arbitraries, wide strings, Variant, undefined type arrays, records, interfaces, and dynamic arrays [4], last Tobject.FreeInstance calls MemoryManager .Freemem releases the memory space occupied by the object itself. That is interest, the object release service is used by the object to create a service, the function is one or one, is there a very neat feeling? Objects created service object release service System._ClassCreateSystem._ClassDestroySystem._AfterConstructionSystem._BeforeDestructionTObject.AfterConstruction (virtual) TObject.BeforeDestruction (virtual) TObject.NewInstance (virtual) TObject.FreeInstance (virtual) TObject.InitInstanceTObject.CleanupInstanceMemoryManager.GetMemMemoryManager.FreeMem Another point Note, usually we don't call DESTROY directly to release objects, but call TOBJECT.FREE, which checks whether the object reference is NIL before the object is released. Related TOBJECT methods: TOBJECT = Class // ... Procedure Free; Procedure Cleanupinstance; Procedure BeforeStruction; Virtual; DESTRUCTOR DESTROY; Virtual; // ... end;
Procedure_classdestroy (Instance: Tobject): TOBJECT; 5. Object; 5, object message assignment service message assignment services typically think is closely combined with the Windows platform, but in TOBJECT It is considered that the message assignment is independent of the platform, because as long as the message with MessageID can be configured correctly, it is greatly scaled, which greatly expands the definition of Windows on the message. About the Delphi message mechanism can read my article "Delphi Message Mechanism Learning Notes" . Related TOBJECT methods: TOBJECT = Class // ... procedure dispatch (var message); Virtual; procedure; virtual; // ... end; six, virtual method table and dynamic method form Delphi virtual method It is achieved by a virtual method table (VMT) or dynamic method table (DMT) or Dynamic Method Table, referred to as DMCI. The pointer to all the virtual methods declared by the Class and its base class in VMT [4]. Each class has a unique VMT and is determined during compilation, not like Li Wei's "Inside VCL" is created in memory when the first class object is created in the first class object ( Why will Li Wei make such a low-level error?), All the objects created by this class share the same VMT. In addition to an imaginary method, VMT also includes other information about a class [4]. A VMT can derive a lot of stories, this is not my notes all accommodated, and the details of the VMT is recommended to work with the article to learn from the reference. DMT is another way to achieve the virtual method in delphi. The use of DMT can effectively reduce the volume of VMT, but the execution efficiency is slightly poor. Few data is very clear about the execution of DMT, but will you get answers in my notes, ready? Come with me! The DMT stores a dynamic method and message handle declared by a class, but does not include a method of inheriting from ancestral class. Methods with keyword Dynamic or Message are implemented in dynamic methods. The DWORD value taken from the VMT VMTDYNAMICTABLE offset is the DMT pointer to the class.
The DMT structure is undocumented in delphi, but it is easy to write the logical layout in the memory by reading the VCL source and Debug: Type TDYNMETHODTABLE = PACKED Record Count: Word; Indexes: Packed Array [1..count] of smallint ; Addresses: packed array [1..Count] of Pointer; end; if we declare the following categories: TMyObject = class private procedure DM1; dynamic; procedure DM2; dynamic; procedure WMCommand (var Message); message WM_COMMAND; public procedure DM3; Dynamic; END; Any method with keywords with Dynamic will be conferred by the compiler to give a number in their order declared in the class, or the ID, the number is represented by a smallint value, the order is $ fff, $ fffe, $ FFFD ... When the compiler encounters Message, the number of the method uses the message ID, such as WM_Command, represents $ 0111. We can easily describe the DMT of this class according to the above memory layout: 4 // Count = 4, including 4 dynamic methods $ fff // DM1 number $ fffe // DM2 number $ 0111 // WMCommand message ID $ fffd // DM3 number DM1 Entry address DM2 Entry address WMCOMMAND Entry address DM3 entry address How is these methods called? The method for taking the Dynamic keyword is to be implemented by calling System._CallDynainSt.
Usually: MOV EAX, EBX; EAX = Point To Current Objectmov Si, $ FFFF; Si is the method number call @calldynainst and system._calldynainst will then call the system.getdyNamethod to get the corresponding method number method inlet address, and jump directly to the method Entrance address, executing this method: procedure _calldynainst; ASM PUSH EAX PUSH ECX MOV EAX, [EAX]; Note When calling, VMT Call getDynamethod pop ECX POP ESIX JE @@ Abstract JMP ESI; ESI point to dynamic method entrance address @@ Abstract: POP ECX JMP_ABSTRACTERROREND; System.getDynamethod Looking for the number of the dynamic method in the DMT, if you find it, get the entry address of the method from the DMT; if you can't find it, continue in the DMT of the parent class. The method number is numbered until TOBJECT's DMT (TOBJECT points to the pointer of the DMT is nil, cycled to this naturally stop here): procedure getdyNamethod; {function getDynamethod (vmt: tclass; selector: smallint): Pointer;} asm {-> EAX VMT Of class} {si Dynamic method index} {<- esi pointer to routine} {zf = 0 if Found} {TRASHES: EAX, ECX} Push EDI X CHG EAX, ESI JMP @@ Havevmt @@ OuterLoop: MOV ESI, [ESI] @@ Havevmt: Mov Edi, [ESI] .VMTDYNAMICTABLE TEST EDI, EDI JE @@ Preent Movzx ECX, Word PTR [EDI] Push ECX Add Edi , 2 repne scasw je @@fact: MOV ESI, [ESI] .Vmtparent test esi, esi jne @@ outerloop jmp @@ exit
@@fact: Pop Eax, Eax Sub Eax, ECX {this will always clear the z-flag!} MOV ESI, [EDI EAX * 2-4] @@ EXIX * 2-4] @@ EXIT: POP Ediend; here to say a sentence, see The code of System.getDynamethod is simply like appreciating an art. The code is optimized by Borland's engineers, which uses a lot of skills, you need to slowly experience, in short! As for the method similar to WMCommand similar to TMYObject, it is usually sent by TOBJECT.DISPATCH, the principle is completely with the above process, but the message ID is retrieved in the DMT instead of the method number. See the TOBJECT.DISPATCH source and my "Delphi Message Mechanism Learning Notes".
references
1. Li Wei. "In-depth core-VCL architecture analysis" Chapter 2, 2004.1
2. Savetime. "Delphi's Object Mechanism", Jan 2004
3. Li Wan. "Wu Delphi" is the second chapter - "Delphi's Atom World"
4. Ray Lischner. "Delphi in a nutshell", 2000