zoukankan      html  css  js  c++  java
  • Delphi 的RTTI机制-2

    TStream 在对象持续机制扮演的角色是提供一种存储媒介,由 TFiler 对象使用。TStream 是一个虚类,它定义了数据的流式读写方法。它的继承类 TFileStreamTMemoryStreamTResourceStream 等实现对不同媒体的读写。对象的 persistent 信息可以存储在任何TStream 类中,也可以从任何 TStream 中获得。由于 Delphi 缺省的对象信息存储在应用程序的资源段中,因此,可以从程序的资源段中读取数据的 TResourceStream 类就显得更加重要。

    TStream
    定义两个读写缓冲的方法:ReadBuffer WriteBuffer。这两个方法封装了 TStream.Read TStream.Write 纯虚方法(必须被后继类重载)

      { TStream }
      procedure ReadBuffer(var Buffer; Count: Longint);
      procedure WriteBuffer(const Buffer; Count: Longint);

    可以看到这两个方法的 Buffer 参数都是无类型的,也就是使用引用的方式传入的,所以不管是使用单个字符或自定义的结构都是正确的(当然,不能使用常量)Count 指示要读或写入的 Buffer 的大小(Bytes)

    TStream
    还定义了两个元件信息的读写方法:ReadComponent WriteComponent。由于 WriteComponent 通常是由 Delphi IDE/编译器调用的,很难跟踪它的执行过程,所以我们以后主要考察 ReadComponent 方法。我们可以很容易想像这两个方法互为逆过程,理解了其中一个也就能知道另一个所做的工作。

      { TStream }
      function ReadComponent(Instance: TComponent): TComponent;
      procedure WriteComponent(Instance: TComponent);

    TStream.ReadComponent
    创建了一个 TReader 对象,将自己的对象地址作为参数传递给 Reader,并调用 Reader.ReadRootComponent 创建对象实例。

      function TStream.ReadComponent(Instance: TComponent): TComponent;
      var
        Reader: TReader;
      begin
        Reader := TReader.Create(Self, 4096);        // 4096
    是缓冲区大小
        Result := Reader.ReadRootComponent(Instance);
        Reader.Free;
      end;

    TStream
    把自己的对象句柄交给TReader 之后,就成了 TReader 读取对象属性资料的来源。此后 TStream 对象只由 TReader 来掌控,自己不再主动进行其它工作。

    ====================================
    TReader Class TReader.ReadRootComponent 方法
    ===============================================================================
    TReader
    TWriter 都是从 TFiler 继承下来的类。TFiler 是个纯虚类,它的构造函数被 TReader TWrite 共享。TFiler.Create 先把 Stream 参数保存在 FStream 字段中,然后生成一个自己的缓冲区:

      constructor TFiler.Create(Stream: TStream; BufSize: Integer);
      begin
        FStream := Stream;          //
    保存 stream 对象
        GetMem(FBuffer, BufSize);   //
    创建自己的缓冲区,加速数据访问
        FBufSize := BufSize;        //
    设置缓冲区大小
      end;

    上面说到 TStream.ReadComponent 在创建 TReader 对象之后,立即调用 TReader.ReadRootComponent 方法。TReader.ReadRootComponent 方法的功能是从 stream 中读取 root class 对象的属性。并返回该对象的指针。

      { TReader }
      function ReadRootComponent(Root: TComponent): TComponent;

    ReadRootComponent
    先调用 TReader.ReadSignature

    TReader.ReadSignature
    方法从 stream 中读取 4 字节的内容,如果读出来的内容不是 'TPF0',则触发异常(SInvalidImage),表示该 stream 的内容是错误的。'TPF0' 就是 root class 对象的标记。

    然后 ReadRootComponent 调用 ReadPrefix 读取元件的继承信息。

    如果 Root 参数是 nil,也就是说 Root 对象还没被创建,则直接从流中读取 Root 的类名,再使用 FindClass 函数找到该类在内存中的地址,并调用该类的构造函数创建 Root 的实例。如果 Root 实例已存在,则调用内嵌的 FindUniquName 函数检查 Root.Name 是否与已有的实例重复,如有重复则在 Root.Name 后加上序号使其唯一。

    接下来 ReadRootComponent 调用 Root ReadState 虚方法从流中读取 Root 对象的属性。

    ====================================
    TReader.ReadPrefix 方法
    ====================================
    ReadPrefix
    方法用于读取元件的状态信息,这些信息是由 Writer 在写入元件属性之前写入的。

      { TReader }
      procedure ReadPrefix(var Flags: TFilerFlags; var AChildPos: Integer); virtual;

    Flags
    参数是以引用方式传递的,用于设置元件的在表单中的状态,元件的状态在这里包含三种情况:

      ffInherited
    :表示元件存在于表单的父类之中
      ffChildPos
    :表示元件在表单中的创建次序(creation order)是重要的
      ffInline  
    :表示元件是最上级(top-level)的元件,比如表单或数据模块

    如果元件的状态中包含 ffChildPosReadPrefix 还会读取元件的创建次序值,存放在 AChildPos 参数中。

    ====================================
    TComponent.ReadState 虚方法
    ====================================
    设置 ReadState 方法的主要目的是在读取属性信息的前后可以让元件进行一些处理工作。ReadState Component Writer 需要注意的方法。

      { TComponent }
      procedure ReadState(Reader: TReader); virtual;

    由于 ReadState 是虚函数,在 TControlTWinControlTCustomForm 等后续类中都被重载,进行自己需要的操作(比如 DisableAlignUpdateControlState)

    TComponent.ReadState
    只有一行代码:Reader.ReadData(Self);

    注意:自己重载 ReadState 方法必须调用 inherited

    ===============================================================================
    TReader.ReadData 方法
    ===============================================================================
    上面说到 TComponent.ReadState 又回头调用 TReader.ReadData 方法。它的主要代码如下:

      { TReader }
      procedure TReader.ReadData(Instance: TComponent);
      begin
        ...
        ReadDataInner(Instance);
        DoFixupReferences;
        ...
      end;

    TReader.ReadData
    基本上是个包装函数,它调用 TReader.ReadDataInner 读取 root 对象及 root 所包含的元件的属性信息。

    ===============================================================================
    TReader.ReadDataInner 方法
    ===============================================================================
    ReadDataInner
    负责读取元件的属性和子元件的属性,它的主要代码如下:

      procedure TReader.ReadDataInner(Instance: TComponent);
        begin
        ...
        while not EndOfList do ReadProperty(Instance);
        ...
        while not EndOfList do ReadComponent(nil);
        ...
      end;

    ReadDataInner
    先循环调用 ReadProperty 从流中读取对象的属性,直到遇到 EndOfList 标志(vaNull)。再循环调用 ReadComponent(nil) 读取子元件的信息。这两个方法都是 TReader 的重要方法,后面分两节讨论。ReadDataInner ReadProperty 调用之后还设置了元件的 Parent Owner 关系。

    ===============================================================================
    TReader.ReadProperty 方法
    ===============================================================================
    ReadProperty
    使用 RTTI 函数将从流中读出的数据设置为对象的属性。它先解析从流中读出的属性名称,然后判断该属性是否有 RTTI 信息,如果有则调用 TReader.ReadPropValue 方法从流中读取属性值;如果该属性没有 RTTI 信息,说明该属性不属于 published 段,而是由元件自己写入的,因此调用 TPersistent.DefineProperties 读取自定义的元件信息。ReadProperty 的关键代码:

      procedure TReader.ReadProperty(AInstance: TPersistent);
      begin
        ...
        PropInfo := GetPropInfo(Instance.ClassInfo, FPropName);
        if PropInfo <> nil then                             //
    检查属性 RTTI 信息
          ReadPropValue(Instance, PropInfo)                 //
    从流中读取属性
        else begin
          Instance.DefineProperties(Self);                  //
    调用自定义存储过程
          if FPropName <> '' then PropertyError(FPropName); //
    注意这里
        end;
        ...
      end;

    ReadPropValue
    方法基本上是使用 SetOrdPropSetFloatPropSetStrPropGetEnumValue RTTI 函数设置元件的属性值,它的代码冗长而简单,不再单独列出。下面介绍比较重要的 DefineProperties 函数。

  • 相关阅读:
    一步一步学Silverlight 2系列(22):在Silverlight中如何用JavaScript调用.NET代码
    一步一步学Silverlight 2系列(16):数据与通信之JSON
    一步一步学Silverlight 2系列(9):使用控件模板
    一步一步学Silverlight 2系列(30):使用Transform实现更炫的效果(下)
    一步一步学Silverlight 2系列(31):图形图像综合实例—实现水中倒影效果
    一步一步学Silverlight 2系列(20):如何在Silverlight中与HTML DOM交互(下)
    一步一步学Silverlight 2系列(33):Silverlight 2应用Web Service两例
    一步一步学Silverlight 2系列(14):数据与通信之WCF
    一步一步学Silverlight 2系列(3):界面布局
    一步一步学Silverlight 2系列(6):键盘事件处理
  • 原文地址:https://www.cnblogs.com/luckForever/p/7254569.html
Copyright © 2011-2022 走看看