Current location - Trademark Inquiry Complete Network - Futures platform - How to publish delphi's tinterfacedobject?
How to publish delphi's tinterfacedobject?
Understand delphi Chapter 4 interface.

Not long ago, a friend who made software gave me a riddle. The riddle is "blind date", let me guess a software term. I thought about it for about a minute and guessed that the answer was "object-oriented". I thought it was interesting, so I thought of a riddle to answer him. The riddle is "kiss", let him guess a software term. A minute later, he said humorously, "When you face your beautiful object, you can't help talking to her!" " . We laughed at the same time. Talking and laughing, it seems that our relationship with our program has deepened. For us, software is life.

The concept of the first section interface

The meaning of the word "interface" is too broad and easy to be misunderstood. The interface mentioned here is not to discuss the program interface in the modular design of programs, let alone the interface between computer hardware devices. The interface I am going to talk about now is a programming language concept similar to class, and it is also the basic technology to realize distributed object software.

In DELPHI, interfaces are defined like classes, but instead of using reserved word classes, interfaces are used. Although interfaces and classes have similar definitions, their concepts are quite different.

As we know, a class is an abstract description of objects with the same properties and behaviors. The description of the class is aimed at the objects in the real world. Interfaces do not describe objects, only behaviors. An interface is a description of a behavior method, whether it is an object or something that implements this behavior method. Therefore, interfaces and classes have different starting points, but they look at problems from different angles.

It can be said that interface is a pure technical concept in the development of cross-process or distributed programming technology. The concept of class is a universal way of thinking and the core of object-oriented thinking. However, the concept of interface did develop with the idea of object-oriented software. Using the concept of interface to understand and construct cross-process or distributed software structures is more intuitive and simple than the underlying concepts such as remote procedure call (RPC) directly used in the early days. Because you can understand an interface like an object, and you don't care whether the object is local or remote.

In DELPHI, the interface is declared as interface. The naming principle is: the interface is named with the letter I, just like the class is named with the letter T. Only methods can be defined in the declaration of the interface, but data members cannot be defined. Because the interface is only a description of methods and behaviors, it does not store the attribute state of the object. Although you can define interface properties in DELPHI, these properties must be accessed based on methods.

All interfaces inherit directly or indirectly from IUnknown. IUnknown is the original ancestor of all interface types and has the same status as TObject in the concept of class. The statement that "one interface inherits another interface" is actually wrong. It should be said that "one interface extends another interface". The expansion of the interface embodies a kind of "compatibility", which is single, and it will never happen that one interface is compatible with two parent interfaces at the same time.

Because the interface only describes a set of methods and behaviors, and the implementation of these methods and behaviors must depend on the class. Interface cannot create an instance. There is no such thing as an interface instance. Only classes can create object instances. But there must be an object instance behind an interface, which is the implementer of the interface method, and the interface is the reference of a set of methods of the object.

Conceptually, a class of an object can implement one or more interfaces. The responsibility of a class to an interface is only to implement the interface, and it should not be said that the class inherits one or more interfaces. The two words "realization" and "inheritance" have different meanings and should be distinguished conceptually.

Usually, when you declare an interface, you need a GUID identifier that can uniquely identify the interface type. Interface types will be used by programs distributed in different process spaces or computers, unlike class types that are only recognized and used in program spaces. In order to ensure that an interface type can be uniquely identified anywhere, it is necessary to have an effective method to identify different interfaces. It is impossible to use artificial naming method, and no one can guarantee that the interface you develop will not have the same name as others. Thus, a so-called "global unique identifier" GUID(global Unique Identifier) came into being. It is an identifier randomly generated by a complex algorithm, and its length is 16 bytes, which can ensure that the identifiers generated anywhere in the world are different. In the editing environment of DELPHI, you can easily generate a GUID identifier and use Ctrl+Shift+G as the unique identifier of the interface.

It is necessary to specify a GUID for the interface. Although you can compile all the time without specifying the GUID of the interface, there will be problems when using some functions related to interface identification and transformation. Especially in the development of COM-based programs, GUID must be indispensable.

The concept of interface is actually very simple, but it plays a key role in distributed software development. Some friends think the interface is more complicated, mainly because they don't understand the concept and principle of the interface. Because people always have a sense of mystery about what they don't know. This mystery often makes people fear the unknown world. To uncover the mystery of the interface, we must constantly learn and understand the mystery of the interface. In fact, there will be a lot of fun in the process of exploration, right?

I don't know in the second quarter

Because IUnknown is the common ancestor of all interfaces, you must understand it first. Knowing the cause of things can effectively help us understand the process and result of things. The original definition of IUnknown is in the System.pas unit. Because it is defined in the System.pas unit, it must be an original related to the system or compiler. Look at the definition of IUnknown. It's very simple. There are only six lines in total.

IUnknown = interface

[' { 000000000-0000-0000-C000-00000000046 } ']

Function QueryInterface (constant IID:TGUID;; out Obj):HResult; stdcall

function _ AddRef:Integer; stdcall

Function _Release: integer; stdcall

End;

However, these six lines of code can be the basis of the interface world. Three of the interface methods contain simple and profound philosophies, and understanding these philosophies will benefit us a lot when writing interface-based programs.

IUnknown's three interface methods are the methods that every interface object class must implement, and they are the basic methods of interface mechanism. Why are these three methods the basis of the interface mechanism? Listen to my long journey.

Let's talk about the QueryInterface method first. We know that an object class can implement multiple interfaces. Any interface object must implement IUnknown interface. So as long as you get an interface pointer, you can definitely call the QueryInterface method through this interface pointer. And call QueryInterface to find out which interfaces this interface pointer has implemented. This is very important for the interface programming mechanism. Judging whether the interface pointer realizes the interface function, interface matching and conversion between different interface types are all related to the QueryInterface method.

QueryInterface has two parameters and a return value. The first parameter is the identification of the interface type, that is, the GUID identification of 16 bytes. Because DELPHI compiler knows what GUID each interface corresponds to, it can directly use an identifier like ImyInterface as the first parameter. If the interface supports the interface type specified by the first parameter, the obtained interface pointer is sent back to the caller through the second parameter Obj, and the return value is S_OK.

From here, we can also see why the GUID identification should be specified for the interface. Because the QueryInterface method needs such identification, and it is the basis of interface and matching transformation mechanism.

Next, let's talk about the polar coordinate methods of _AddRef and _Release. The _AddRef and _Release interface methods are the methods that every object class to be connected must implement. _AddRef is to increase the reference count of the interface object, and _Release is to decrease the reference of the interface object. If the reference count of an interface object is zero, the interface object is destroyed and space is freed. This is a basic principle required by the interface mechanism, which is as simple as 1+ 1=2, and needs no profound explanation. Mathematicians will be interested in studying why one plus one equals two. But mathematicians have a thorough understanding of 1+ 1=2. Similarly, a deep understanding of the interface object reference mechanism will make us understand a lot of truth and bring benefits to our development work.

A master once said: interfaces are all references!

To understand this sentence, we must first understand the concept of "quotation". "Reference" means "borrowing", which indicates the reference relationship. Only when the quoted party finds the network of the quoted party can the quoted party be the real center. Because the object can be found through this reference relationship, the reference is actually the identity representative of the object. In programming, a reference is actually a pointer, which uses the address of the object as the identification representative of the object.

In programs that are not based on interface mechanism, there is no need to manage the reference relationship of objects. Because all instances of non-interface objects are in the same process space, the process of establishing, using and releasing objects can be strictly controlled through programs. However, in the program based on interface mechanism, the creation, use and release of objects may appear in the same process space, or in different process spaces, or even in two computers thousands of miles apart on the Internet. Establish an interface in one place, and the object that may realize this interface exists in another place; After an interface is established in one place, it may be used in another place. In this case, it is very difficult to use traditional programs to control the creation and release of objects. There must be another agreed mechanism to handle the creation and release of objects. So this heavy responsibility falls on IUnknown's _AddRef and _Release.

This interface object reference mechanism requires that the program where the object instance is located is responsible for the establishment and release of the interface object, that is, the object class that implements the interface is responsible. Whenever an interface of an object is referenced anywhere, the _AddRef method of the interface must be called. When the object is no longer referenced, you must also call the _ Release method of the interface. Once an object instance finds that it is no longer referenced anywhere, it will release itself.

_AddRef and _Release methods are just to solve the problem of interface object instance space management, and become the methods that all interface object classes must implement.

Section 3 Life and Death of Interface Objects

At first glance, the title of this section seems a bit scary. How can interface objects be associated with life and death? Is the life and death of the interface object really that important? A good ruler should care about people's life and death. Similarly, a good programmer should also care about the life and death of the object. The interface objects are vagrants in the distributed network, and we should care more about their life and death!

Because the interface object is established with the generation of the interface reference and dies with the end of the interface reference. Using interface in DELPHI, it seems that no one cares about how the object that implements the interface comes from and how it dies. This is the simplicity of DELPHI's interface, and it is also the goal it pursues when solving the problem of using interface mechanism. When an interface is needed, there will always be an object for her. Once any interface is no longer referenced, this object will die without complaint and love, and will never drag down a byte of system resources. It's a bit like "the silkworm will weave until it dies in spring, and the candle will cry the wick away every night".

Because the life and death of an interface object is directly related to the number of interfaces that reference the object, it is the key to understand the life and death of an interface object to study under what circumstances interface references will be increased and decreased.

Now let's implement the simplest interface object class TIntfObj, which only implements the three basic methods defined in the IUnknown interface. A friend knew at a glance that this class actually copied part of the code of TInterfacedObject class in DELPHI. It's just that we added some information output statements to the _AddRef and _Release methods respectively, so that we can discuss the life and death of the interface object. Please check the following procedures:

Program ProgramA

use

System, dialog box;

type

TIntfObj = class(to object,IUnknown)

protect

FRefCount: integer;

Function QueryInterface (constant IID:TGUID;; out Obj):HResult; stdcall

function _ AddRef:Integer; stdcall

Function _Release: integer; stdcall

End;

Function TIntfObj query interface(const IID:TGUID; out Obj):HResult; stdcall

constant

e _ no interface = HResult($ 80004002);

begin

if GetInterface(IID,Obj)then Result:= 0 else Result:= E _ no interface;

End;

Function TIntfObj _ AddRef:Integer; stdcall

begin

INC(fref count);

ShowMessage(Format ('Increase reference count to% d.', [frefcount]));

Result: = FRefCount

End;

Function TIntfObj _Release: integer; stdcall

begin

DEC(fref count);

If FRefCount & lt& gt then 0

ShowMessage (format ('reduce reference count to %d', [FRefCount]))

Otherwise start.

Destroy;

ShowMessage ('reduces the reference count to 0 and destroys the object. );

End;

Result: = FRefCount

End;

defined variable

ao object:TIntfObj;

aInterface:I unknown;

IntfObjLife process;

begin

ao object:= TIntfObj。 Create;

aInterface:= a object; //Add reference

aInterface:= nil; //Reduce the reference once

End;

begin

IntfObjLife

End.

We need to use single-step debugging function to study the relationship between the increase and decrease of interface reference count and interface life and death. Therefore, it is suggested that you clear the optimization items of the compiler page in the options to avoid the compiler from optimizing the instructions we need.

Please debug the code step by step when the program executes the three lines of the IntfObjLife subroutine. You will find that when an interface type variable assignment occurs, it will cause the interface reference count to increase or decrease.

execute statement

aInterface:= a object;

Message "Reference count increased to 1." Will appear, indicating that the interface reference has been added once.

And execute the statement.

aInterface:= nil;

"The reference count is reduced to 0 and the object is destroyed." will appear, indicating that the interface reference is reduced to zero and the interface object is deleted.

So we can draw a conclusion that the reference count of interface objects will increase when assigning reference values to variables of interface types; When the reference value of the interface type variable is cleared (assigned to nil), the reference count of the interface object will be reduced.

Take a look at the following code to deepen our understanding of this conclusion.

defined variable

ao object:TIntfObj;

InterfaceA,InterfaceB:I unknown;

……

ao object:= TIntfObj。 Create;

interface a:= a object; //Reference added to 1

interfaca:= interfaca; //The reference increased to 2, but immediately decreased to 1.

InterfaceB:= InterfaceA; //Reference increased to 2

inter faca:= nil; //Reference reduced to 1

InterfaceB:= InterfaceA; //Reduce the reference to 0 and release the object.

……

Whether to assign interface objects to variables, assign interface variables to interface variables, and assign nil to interface variables all confirm this conclusion. Interestingly, when the sentence InterfaceA := InterfaceA is executed, the reference of the interface object first increases, and then immediately decreases. Why is this happening? Leave it to yourself to think!

Then, let's look at the following code:

IntfObjLife process;

defined variable

ao object:TIntfObj;

aInterface:I unknown;

begin

ao object:= TIntfObj。 Create;

aInterface:= a object;

End;

The difference between this process and the previous process is that variables are defined as local variables, and finally interface variables are not given zero values. We find that before the program runs to the subroutine end statement, the reference of the interface object is reduced to 0 and released. Why is this?

We know that variables have ranges. The scope of global variables is anywhere in the program, while the scope of local variables is only in the corresponding subroutine. Once a variable leaves its scope, the variable itself does not exist, and the value it stores is even more meaningless. Therefore, when the program is about to leave the subroutine, the local variable aInterface will no longer exist, and its stored interface object reference value will lose its meaning. Smart DELPHI automatically reduces the reference count of interface objects to ensure that the program correctly manages the memory space of interface objects in layer-by-layer call and return.

Therefore, we can draw a new conclusion: when any interface variable is out of its scope, the reference count of related interface objects will decrease.

It should be noted that the parameter variable of a subroutine is also a variable, and its scope is also within the scope of the subroutine. When calling a subroutine with interface type parameters, the reference count of related interface objects will increase due to the passing of parameters, and will decrease when the subroutine returns.

Similarly, if the return value of a subroutine is an interface type, the range of the return value is the range from the return point of the main caller to the end statement of the main caller. This situation will also cause the reference count of interface objects to increase or decrease.

It's time to summarize the life and death of the object. After the final conclusion, we can draw the following principles:

1. When assigning reference values of interface objects to global variables, local variables, parameter variables and return values, the reference count of interface objects will definitely increase.

2. Before the original interface reference value of a variable changes, the reference count of its associated object will decrease. Assigning nil to variables is a special case of assigning and modifying interface references, which only reduces the reference count of the original interface objects and does not involve new interface references.

3. Global variables, local variables, parameter variables, return values and other elements that store interface reference values will automatically reduce the reference count of interface objects when they exceed their scope.

4. When the reference count of the interface object is zero, the memory space of the interface object is automatically released. (In some middleware systems that use object caching technology, such as MTS, this principle may not be followed. )

Need to remind you that once the established interface object is handed over to the interface, the life and death of the object is entrusted to the interface. Just like marrying your precious daughter to an honest man, you should trust him completely and believe that he can take good care of her. From now on, the contact with the object should be through the interface, rather than directly dealing with the object. You know, bypassing the son-in-law and directly intervening in the daughter's affairs may cause big problems. Do not believe, let's take a look at the following code:

Family planning;

type

IHusband = interface

Function getsomething: string;

End;

TWife = class(TInterfacedObject,IHusband)

private

f something:string;

public

The constructor create (something: string);

Function getsomething: string;

End;

Constructor TWife. Create (something: a string);

begin

Inheritance and creation;

F Something:= something;

End;

Function TWife. get something:string;

begin

Result: = FSomething

End;

Program hubbanddo (ahusband: ihusband);

begin

End;

defined variable

the life:tw ife;

the husband:IHusband;

begin

the life:= TWife。 Creation ('super wealth');

TheHusband:= the life; //the object TheWife is delegated to the universal interface variable TheHusband.

Husband: = zero; //Clear the interface reference and the object disappears.

Life. GetSomething// There must be an error accessing the object directly!

the life:= TWife。 Creation ('super wealth');

Husband's behavior (the life); //The object is delegated to the parameter interface variable aHusband, and disappears when the object returns.

Life. GetSomething// There must be an error accessing the object directly!

End.