delphi RTTI 2

来源:互联网 发布:c语言缺省参数 编辑:程序博客网 时间:2024/06/05 19:23

Delphi运行期间,一个对象变量实际上是一个四字节指针,指向内存中此对象具体占据的一片区域,而区域的首个四字节又是一个指针指向该类的VMT,所有该类的实例对象的区域的首四字节指针都指向同一个VMT,故此一个VMT基本上就可以代表类本身。而每个类的VMT前面(VMT指针所指处的负偏移处)保存了该类的一些运行期信息,包括-44(vmtClassName)处的指向ClassName的字符串指针,-40(vmtInstanceSize)处的对象实例大小InstanceSize等。而本文专门讲述其-60(vmtTypeInfo)处的TypeInfo/ClassInfo指针所指的、本类的属性的RTTI信息。

以上内容转自:http://www.cnblogs.com/keyvip/archive/2010/12/14/1905432.html


MethodAddress
用法:通过方法名对发布区的方法地址的读取,返回方法的地址(必须是publish的过程才可以的).
定义:class function TObject.MethodAddress(const Name: ShortString): Pointer;
(可以默认是在发布区的)


TMethod:是记录类型。所在单元system.pas
定义
  TMethod = record
    Code, Data: Pointer; //方法的入口地址,类实例指针
  end;
用法:只是要记录一个类的函数地址和类对象的地址


TNotifyEvent:是一个类,对象的过程类。所在单元classes.pas
定义: TNotifyEvent = procedure(Sender: TObject) of object;
作用:可以利用 @vEvent,取得类中所有方法的地址。


SetMethodProp
作用: 函数用于设置 Method 类型的属性值
定义:procedure SetMethodProp(Instance: TObject; const PropName: string;const Value: TMethod);




类引用
type
  TClass = class of TObject; //TClass不仅缺省代表一个类,而且还(主要)代表了类的类型,可以用它来定义类变量,实现一些类级别的操作。


var
  TMyClass: TClass;  //一个 VMTptr 变量
  MyObject: TObject; //VMTPtr 常量,值为TObject
begin
  TMyClass := TObject; //被赋值为TObject
  MyObject := TObject.Create;
  MyObject := TClass.Create;//上面两句的汇编代码完全相同
  MyObject := TMyClass.Create;
end;


VMT
在创建一个类的实例之后,编译器在该对象的内存空间的首4个字节安插一个指针,该指针指向的地址称为VMT,
其实只是真正VMT的一部分,也就是用户定义的第一个虚方法的位置,这些指针的值和指针所指的内容在编译后就确定了。
如果以这个位置为原点,向正方向即刚才所说的VMT,而向负方向,则是语言定义的另一些信息所在地址(vmtClassName类名-44,
vmtDestroy析构函数地址-4,vmtTypeInfo类信息指针-60),不过一般不使用数值来访问这些类信息,而是通过 System.pas 中定义的以
vmt 开头的常量,如 vtmClassName、vmtParent 等来访问。
在Object Pascal中,所有类实例都会有这么一个指向VMT的指针。如果没有在类中声明虚方法,则该指针为nil。


类的方法
类的方法有两种:对象级别的方法和类级别的方法。两者的 Self 指针意义是不同的
在对象级别的方法中 Self 指向对象地址空间,因此可以用它来访问对象的成员函数;
在类级别的方法中Self 指向类的 VMT,因此只能用它来访问 VMT 信息,而不能访问对象的成员字段
(比如TObject.ClassInfo,是一个类级别的方法,self的指针指向VMTptr;TObject.ClassType 是对象级别的方法,Self 的值是指向对象内存空间的指针)




TTypeInfo结构描述了RTTI的基本类型信息,是 RTTI 信息的结构.而不光是针对类的。一个类的VMT首部偏移-60(vmtTypeInfo)处的四字节是一个TypeInfo/ClassInfo指针,指向一个


TTypeInfo结构
  PPTypeInfo = ^PTypeInfo;
  PTypeInfo = ^TTypeInfo;
  TTypeInfo = record     //基本类型信息
    Kind: TTypeKind;     // 该类型信息所描述的类型,是类则为tkClass
    Name: ShortString;
   {TypeData: TTypeData}
  end;


  PTypeData = ^TTypeData;
  TTypeData = packed record  //RTTI 的内容
    case TTypeKind of
      ...
      tkClass: (
        ClassType: TClass;
        ParentInfo: PPTypeInfo; // 指向父类的 TypeInfo 结构
        PropCount: SmallInt;  // 本类的总属性数目,包括父类的属性数
        UnitName: ShortStringBase;// 本类所在的单元名
       {PropData: TPropData});
      ...
  end;


  TPropData = packed record       //所有属性的详细信息
    PropCount: Word;   // 本类的属性数目,不包括父类
    PropList: record end;
    {PropList: array[1..PropCount] of TPropInfo}
  end;


  PPropInfo = ^TPropInfo;
  TPropInfo = packed record  //每个属性信息在内存中的结构就是 TPropInfo
    PropType: PPTypeInfo;
    GetProc: Pointer;
    SetProc: Pointer;
    StoredProc: Pointer;
    Index: Integer;
    Default: Longint;
    NameIndex: SmallInt;
    Name: ShortString;
  end;


1.GetTypeData 
定义:
function GetTypeData(TypeInfo: PTypeInfo): PTypeData; assembler;
asm
        { ->    EAX Pointer to type info }
        { <-    EAX Pointer to type data }
        {       it's really just to skip the kind and the name  }
        XOR     EDX,EDX
        MOV     DL,[EAX].TTypeInfo.Name.Byte[0]
        LEA     EAX,[EAX].TTypeInfo.Name[EDX+1]
end;
作用:从一个类的 TypeInfo/ClassInfo 指针得到一个类的 TypeData 指针.
(就是从TTypeInfo中跳过Kind和Name,直接到TypeData的指针。代码中的注释也说明了这一点.)


2.GetPropInfos
定义:procedure GetPropInfos(TypeInfo: PTypeInfo; PropList: PPropList); assembler;比较长,就不贴具体出来。
      过程将一个类的所有属性信息的地址转存到一个预先分配好的列表中,其内在机制稍微复杂一点,
      简而言之是遍历本类以及父类的属性数组并把遍历到的每一处的属性地址写入列表中。详见注释。
      注意的地方是,这个过程不负责分配该数组的内容,使用前必须根据属性的数量分配足够的空间。
      该数组结束后必须清除分配的内容。GetPropList 实现相同的功能更方便。


3
  function GetPropList(TypeInfo: PTypeInfo; TypeKinds: TTypeKinds;PropList: PPropList; SortList: Boolean): Integer
  function GetPropList(TypeInfo: PTypeInfo; out PropList: PPropList): Integer;
  function GetPropList(AObject: TObject; out PropList: PPropList): Integer;


注意:GetPropList 的内存分配有点混乱,上面第一个 GetPropList 必须自己分配 PPrpList 数组的内存,
后面二个 GetPropList 会自动分配 PPropList 数组的内存。造成这种情况的原因是:
第一个 GetPropList 可以设置 TypeKinds 参数限制只返回指定类型的属性,这样就不能直接得到可能返回的属性数量。
TypeKinds 参数可以设置为 tkAny,表示返回所有数据类型的属性。第一个 GetPropList 函数可以设置 SortList 参数对属性名称进行排序。
它实际上是调用第二个 GetPropList 并调用 SortPropList 函数执行排序。
PPropList 不再使用的时候,要记得使用 FreeMem 函数清除数组内存(根据返回值是否大于1)。


4. GetObjectPropClass 函数用于返回对象类型的属性所属的类(class),被 SetObjectProp 函数使用,用于参数检验。
 function GetObjectPropClass(Instance: TObject; PropInfo: PPropInfo): TClass;
 function GetObjectPropClass(Instance: TObject; const PropName: string): TClass;
 function GetObjectPropClass(PropInfo: PPropInfo): TClass;




5.PropType 函数用于获得属性的数据类型。
function PropType(Instance: TObject; const PropName: string): TTypeKind;
function PropType(AClass: TClass; const PropName: string): TTypeKind;




6
function PropIsType(Instance: TObject; const PropName: string;TypeKind: TTypeKind): Boolean;
function PropIsType(AClass: TClass; const PropName: string;TypeKind: TTypeKind): Boolean;
PropIsType 判断属性是否属于某种数据类型。它调用 PropType 实现功能。


7.IsPublishedProp 函数用于判断属性是否是 published 属性,它通过检查该属性 RTTI 指针是否等于 nil 来实现功能。
 function IsPublishedProp(Instance: TObject; const PropName: string): Boolean;
 function IsPublishedProp(AClass: TClass; const PropName: string): Boolean;
 IsPublishedProp 函数没有被 VCL 使用。


8

IsStoredProp 函数




9
Tobject.ClassInfo和TObject.ClassType一个是类方法,一个是对象的方法。
TObject.ClassInfo 返回的 Pointer 指针,实际上是指向类的RTTI结构的指针,即是TTypeInfo的指针。
TObject.ClassType,根据源码可以知道,事件上是指向该对象的指针。
转换:var obj:tobject;
          obj.ClassInfo = obj.ClassType+vmtTypeInfo;


参考资料
http://blog.csdn.net/coolbaby/article/details/503910
http://www.cnblogs.com/keyvip/archive/2010/12/14/1905432.html
http://blog.csdn.net/stevenldj/article/details/7166455 用来读取指定类的信息。
http://blog.csdn.net/easyboot/article/details/8004954  Delphi 遍历类中的属性 .
http://blog.sina.com.cn/s/blog_5591c079010007ce.html
http://www.98exe.net/Article/c/2006-10-19/2015.html


原创粉丝点击