我现在需要模拟类的创建过程,看System._ClassCreate方法中可以实现这个功能,就是看不懂汇编代码...哪位达人给解释下?

解决方案 »

  1.   

    BeforeDestruction 是不可避免要被调用的,只不过有个隐含参数,不该继续执行的时候就跳出而已
      

  2.   

    我是用下面的代码来模拟类的创建过程的:
    function InternalCreateInstance(classType: TClass;
      constructorMethod: TRttiMethod; const arguments: TArray<TValue>): TObject;  // TEMP
      procedure DestroyInstance(instance: TObject);
      begin
        instance.Destroy;
      end;
    begin
      Result := classType.NewInstance;
      try
        constructorMethod.Invoke(Result, arguments);
      except
        on Exception do
        begin
          DestroyInstance(Result);
          raise;
        end;
      end;
      try
        Result.AfterConstruction;
      except
        on Exception do
        begin
          Result.BeforeDestruction;
          DestroyInstance(Result);
          raise;
        end;
      end;
    end;
    我测试过,如果在构造函数里面抛出异常的话,BeforeDestruction并不会被执行,但Destroy方法本身会被执行。
      

  3.   

    已经告诉你了,destructor 有个1字节的隐含参数,不会自己试么?
      

  4.   

    哈哈,谢谢大侠的指点,确实如此。type
      TDestroyMethod = procedure(value: ShortInt) of object;
    var
      obj: TObject;
      method: TMethod;
    begin
      obj := TObject.Create;
      method.Code := @TObject.Destroy;
      method.Data := obj;
      TDestroyMethod(method)(1); // 大于0就不会调用BeforeDestruction
    end;
    再请教一个相关的问题,如何调用对象实例相关的VMT中的Destroy方法?必须要用汇编吗?
      

  5.   

    你调了多带了一个隐含参数的 destructor 用汇编了么?这两者之间有差别么?btw,总结的不对,inherited Destroy 一般就是0,不会再多调一次
      

  6.   

    这两个不一样,我给TMethod传入的是TObject.Destroy的绝对地址,如果我不知道对象的具体类型的话就只能通过vmt来调用了。(汇编里面有类似CALL DWORD PTR [ECX] + VMTOFFSET TObject.Destroy的代码)对此我有个疑问,正常的类创建流程应该是:NewInstance->Create->AfterConstruction;析构:BeforeDestruction->Destroy。
    我调试过,如果直接在构造函数内抛出异常,那么AfterConstruction自然不会执行,而且编译器会自动执行子类的Destroy,此时并不会执行BeforeDestruction。
    所以我才猜想在调用Create函数的异常处理代码中直接调用Destroy方法,并传入隐藏的参数。不知道Seamour大侠是怎么判断的?
      

  7.   

    咋不一样,都是一个函数指针一个对象指针的事儿。如果想用 method 的话,完全可以直接取对象虚函数的地址,多一层 cast 而已。不想用的话直接一个函数指针调就完了,这种活儿根本没内嵌汇编的必要我不用判断,直接看反汇编就完了。
    不看反汇编也很不难猜处理过程,首先就是肯定是有通用性的,否则编译器也太累。然后就是要避免一些特殊情况引发的逻辑错误,最重要的是 inherited 会不会导致重复调用 BeforeDestruction 和 FreeInstance。如果让我来设计的话,我会采用怎样的流程,想明白这个别的也就都好办了