1

如果我理解正确,这很好:

type
  IMyInterface = interface['{60E314E4-9FA9-4E29-A09A-01B91F2F27C7}']  
    procedure MyMethod;
  end;

type
  TMyIClass = class(TInterfacedObject, IMyInterface)
  public
    procedure MyMethod;  // Forget the implementations in this example
  end;

var
   lMyIClass: IMyInterface;
   lSupports: Boolean;
begin
   lMyIClass := TMyIClass.Create;
   lSupports := Supports(lMyIClass,IMyInterface);
   Memo1.Lines.Add('lMyIClass supports IMyInterface: ' + BoolToStr(lSupports,true));
   if lSupports then DoSomethingWith(lMyIClass);

现在我有一个实现多个接口的类:

type
   IFirstInterface = interface['{4646BD44-FDBC-4E26-A497-D9E48F7EFCF9}']
     procedure SomeMethod1;
   end;

   ISecondInterface = interface['{B4473616-CF1F-4E88-9EAE-1AAF1B01A331}']
     procedure SomeMethod2;
   end;

   TMyClass = class(TInterfacedObject, IFirstInterface, ISecondInterface)
     procedure SomeMethod1;
     procedure SomeMethod2;
   end;

我可以调用另一个重载的 Support() 返回接口并用它做一些事情):

var
   MyClass1,MyClass2 : TMyClass;
   i1: IFirstInterface;
   i2: ISecondInterface;
   bSupports: Boolean;
begin
    Memo1.Clear;
    MyClass1 := TMyClass.Create;

    bSupports := Supports(MyClass1,IFirstInterface,i1);  
    if bSupports then 
    begin
        Memo1.Lines.Add('MyClass1 supports IFirstInterface');
        DoSomethingWith(i1);
    end
    else
        Memo1.Lines.Add('MyClass1 does not support IFirstInterface');

    bSupports := Supports(MyClass1,ISecondInterface,i2);    
    if bSupports then 
    begin
        Memo1.Lines.Add('MyClass1 supports ISecondInterface');
        DoSomethingElseWith(i2);
    end
    else
        Memo1.Lines.Add('MyClass1 does not support ISecondInterface');

    MyClass1 := nil;
    i1 := nil;
    i2 := nil;
    MyClass2 := TMyClass.Create;

    bSupports := Supports(MyClass2,IFirstInterface,i1);

    if bSupports then 
    begin
        Memo1.Lines.Add('MyClass2 supports IFirstInterface');
        DoSomethingWith(i1);
    end
    else
        Memo1.Lines.Add('MyClass2 does not support IFirstInterface');

    bSupports := Supports(MyClass2,ISecondInterface,i2);
    if bSupports then 
    begin
        Memo1.Lines.Add('MyClass2 supports ISecondInterface');
        DoSomethingElseWith(i2);
    end
    else
        Memo1.Lines.Add('MyClass2 does not support ISecondInterface');

我对此有三个问题:

  1. 现在MyClass1, MyClass2是对象类型,而不是简单示例中的接口类型。这个可以吗?

  2. 我应该 Free() 还是“nil” MyClass1 或者甚至不理会它?

  3. 在处理了 2. 之后,ix:= nil关于引用计数是否仍然需要这两个语句?

4

2 回答 2

4

一个常见的建议是永远不要将对象引用与接口引用混合。这意味着如果您需要实例化一个类并使用它的任何接口,最好不要通过对象引用类型来引用它。TMyClass通过将变量更改为类型而不是接口类型,您违反了该建议。将它们声明为接口变量;我会用IUnknown.

这个建议的原因是对象引用与接口引用不同。编译器总是为接口变量插入引用计数代码,并且该代码忽略了程序中其他任何地方的任何对象引用。由于引用计数,对象引用变量在更改某些接口变量后可能会变得无效,并且在编写程序时很容易忽略这一点。如果您从来没有对象引用变量,那么您不必担心这种可能性;接口引用应始终有效。

如果MyClass1是对象引用,则在将其分配给接口变量后不应调用它。Free这是您的一些代码,带有对象的引用计数注释:

MyClass1 := TMyClass.Create;  // initialized to 0

bSupports := Supports(MyClass1,IFirstInterface,i1); // incremented to 1
if bSupports then 
begin
    Memo1.Lines.Add('MyClass1 supports IFirstInterface');
    DoSomethingWith(i1);
end
else
    Memo1.Lines.Add('MyClass1 does not support IFirstInterface');

bSupports := Supports(MyClass1,ISecondInterface,i2); // incremented to 2
if bSupports then 
begin
    Memo1.Lines.Add('MyClass1 supports ISecondInterface');
    DoSomethingElseWith(i2);
end
else
    Memo1.Lines.Add('MyClass1 does not support ISecondInterface');

MyClass1 := nil; // still 2
i1 := nil; // decremented to 1
i2 := nil; // decremented to 0; the object gets destroyed

如果你MyClass1.Free在任何时候调用,你的程序就会崩溃。自己释放对象不会更改i1or中的值i2,因此编译器自动插入的引用计数代码仍会执行。它会尝试减少已释放对象的引用计数,这显然不好。

但是假设您等到清除and之后,如以下代码所示:i1i2

i1 := nil;
i2 := nil;
MyClass1.Free;

那还是错的。清除变量会将引用计数设置为 0,因此对象在分配给i2;时会被销毁。in 的值MyClass1是无效的,所以你也不应该Free在那里调用它。

将对象引用分配给接口引用后,最安全的做法是立即清除对象引用。然后你就不会再想使用它了。

通常不需要清除接口变量。它在其生命周期结束时被自动清除(对于局部变量,当它们在函数结束时超出范围时)。此外,如果你调用Supports并传入一个已经分配的接口引用,它要么会接收到新对象的接口引用,要么会被清除;它不会继续保持之前的值。

也就是说,当您调用 时Supports(MyClass2,IFirstInterface,i1);,无需先清除i1。对 的调用Supports将填充i1对 引用IFirstInterface的对象的引用MyClass2,或者将存储nil在 中i1

于 2013-08-09T17:36:14.527 回答
0

原则上......我同意每个人所说的关于混合接口和对象模型的一切,不要混合它们......

这是让自己陷入困境并在深夜跟踪奇怪的 GPF 的一种简单方法......

但是...(总是有一个但是)...

你可以做到......如果你了解Gotcha的......

  • 如果您创建 TInterfacedObject 并将其传递给变量引用。是否需要释放它取决于您是否从中获取接口。
  • 一旦你从它那里得到一个接口......引用计数开始......你不再负责释放它。对于从 TInterfacedObject 派生的任何对象都是如此
  • 只要“As”在范围内,“As”就会增加接口的引用计数。这意味着如果您没有另一个接口引用......一旦“As”超出范围,您的对象就会再见。

结帐 Button4Click 和 Button5Click...Button4Click 由 Interface 执行...Button5Click 由两者执行。

  IReqBase = Interface(IInterface)
  ['{B71BD1C3-CE4C-438A-8090-DA6AACF0B3C4}']
    procedure FillWithTemplateData;
  end;

  IReqLogIn = Interface(IInterface)
  ['{133D2DFF-670C-4942-A81C-D18CBE825A93}']
    procedure SetupPassword(aUserName, aPassword: string);
  end;

  type
   TWebAct = (ttlogin,
              ttsignin);

  TForm59 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    CheckBox1: TCheckBox;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
  private
    { Private declarations }
    FReqList: Array of IReqBase;
    procedure CreateBaseList;
    procedure ClearBaseList;
  public
    { Public declarations }
  end;

  TJSONStructure = class(TInterfacedObject);

  TReqBaseClass = class of TReqBase;

  TReqBase = class(TJSONStructure, IReqBase)
  private
     token: Int64;
  protected
    class function ReqClass: TReqBaseClass; virtual; abstract;
  public
     Constructor Create; virtual;
     procedure FillWithTemplateData; virtual;
     class function ReqBase: IReqBase;
  end;

  TReqLogin = class(TReqBase, IReqLogIn)
  private
    Fusername,
    Fpassword: String;
    Fmodule  : Integer;
  protected
    class function ReqClass: TReqBaseClass; override;
  public
     Constructor Create; override;
     Destructor Destroy; override;
     procedure SetupPassword(aUserName, aPassword: string);
     procedure FillWithTemplateData; override;
  end;

  TReqSignIn = class(TReqBase)
  private
    Fusername,
    Fpassword: String;
    Fmodule  : Integer;
  protected
    class function ReqClass: TReqBaseClass; override;
  public
     Constructor Create; override;
     Destructor Destroy; override;
     procedure FillWithTemplateData; override;
  end;


var
  Form59: TForm59;

implementation

{$R *.dfm}

procedure TForm59.Button1Click(Sender: TObject);
begin
  Memo1.Lines.Clear;

  IReqBase(FReqList[integer(CheckBox1.Checked)]).FillWithTemplateData;
end;

procedure TForm59.Button2Click(Sender: TObject);
begin
  CreateBaseList;
end;

procedure TForm59.Button3Click(Sender: TObject);
begin
  if CheckBox1.Checked then
    TReqSignIn.ReqBase.FillWithTemplateData
  else
    TReqLogin.ReqBase.FillWithTemplateData;
end;

procedure TForm59.Button4Click(Sender: TObject);
var
  a_IReqBase1: IReqBase;
  a_IReqBase2: IReqBase;
  a_IReqLogIn: IReqLogIn;
begin
  a_IReqBase1 := TReqSignIn.Create;
  a_IReqBase2 := TReqLogin.Create;

  a_IReqLogIn :=  a_IReqBase2 as IReqLogIn;
  a_IReqLogIn.SetupPassword('houseofdexter', 'dexter');

  a_IReqBase1.FillWithTemplateData;
  a_IReqBase2.FillWithTemplateData;
  a_IReqLogIn :=  a_IReqBase2 as IReqLogIn;
  a_IReqLogIn.SetupPassword('houseofdexter', 'dexter');
end;

procedure TForm59.Button5Click(Sender: TObject);
var
  a_ReqBase1: TReqSignIn;
  a_ReqBase2: TReqLogin;
  a_IReqBase1: IReqBase;
  a_IReqBase2: IReqBase;
  a_IReqLogIn: IReqLogIn;
begin
  a_ReqBase1 := TReqSignIn.Create;
  a_ReqBase2 := TReqLogin.Create;

  a_IReqBase1 :=  a_ReqBase1;
  a_IReqBase2 :=  a_ReqBase2;

  a_IReqLogIn :=  a_IReqBase2 as IReqLogIn;
  //note we are working with the object...
  a_ReqBase2.SetupPassword('houseofdexter', 'dexter');

  a_IReqBase1.FillWithTemplateData;
  a_IReqBase2.FillWithTemplateData;
  a_IReqLogIn :=  a_IReqBase2 as IReqLogIn;
  a_IReqLogIn.SetupPassword('houseofdexter', 'dexter');
end;

procedure TForm59.ClearBaseList;
begin
  SetLength(FReqList, 0);
end;

procedure TForm59.CreateBaseList;
begin
  if High(FReqList) = Ord(High(TWebAct)) +1 then
    ClearBaseList;

  SetLength(FReqList, Ord(High(TWebAct)) + 1 );
  FReqList[ord(ttlogin)] := TReqLogin.ReqBase;
  FReqList[ord(ttsignin)] := TReqSignIn.ReqBase;
end;

procedure TForm59.FormCreate(Sender: TObject);
begin
  CreateBaseList;
end;

procedure TForm59.FormDestroy(Sender: TObject);
begin
  ClearBaseList;
end;

{ TReqLogin }

constructor TReqLogin.Create;
begin
  inherited;
  FUserName := 'Rick';
  FPassword := 'Test';
  Fmodule := 100;
end;


destructor TReqLogin.Destroy;
begin
  Form59.Memo1.Lines.Add('Destroyed: ' +ClassName);
  Form59.Memo1.Lines.Add('RefCount: ' + IntToStr(TInterfacedObject(Self).RefCount));
end;

procedure TReqLogin.FillWithTemplateData;
begin
  inherited;
  Form59.Memo1.Lines.Add(Fusername);
  Form59.Memo1.Lines.Add(FPassword);
  Form59.Memo1.Lines.Add(IntToStr(FModule));
end;

class function TReqLogin.ReqClass: TReqBaseClass;
begin
  Result := TReqLogin;
end;

procedure TReqLogin.SetupPassword(aUserName, aPassword: string);
begin
  Fusername := aUserName;
  Fpassword := aPassword;
end;

{ TReqBase }

constructor TReqBase.Create;
begin
  inherited;
  Form59.Memo1.Lines.Add('Created: ' +ClassName);
  Token := -1;
end;

procedure TReqBase.FillWithTemplateData;
begin
  Form59.Memo1.Lines.Add(Self.ClassName);
  Form59.Memo1.Lines.Add('RefCount: ' + IntToStr(TInterfacedObject(Self).RefCount));
  Form59.Memo1.Lines.Add(IntToStr(Token));
end;

class function TReqBase.ReqBase: IReqBase;
begin
  Result := ReqClass.Create;
end;

{ TReqSignIn }

constructor TReqSignIn.Create;
begin
  inherited;
  Form59.Memo1.Lines.Add('RefCount: ' + IntToStr(TInterfacedObject(Self).RefCount));
  FUserName := 'Peterson';
  FPassword := 'TestPW';
  Fmodule := 101;
end;

destructor TReqSignIn.Destroy;
begin
  Form59.Memo1.Lines.Add('Destroyed: ' +ClassName);
  Form59.Memo1.Lines.Add('RefCount: ' + IntToStr(TInterfacedObject(Self).RefCount));
  inherited;
end;

procedure TReqSignIn.FillWithTemplateData;
begin
  inherited;
  Form59.Memo1.Lines.Add(Fusername);
  Form59.Memo1.Lines.Add(FPassword);
  Form59.Memo1.Lines.Add(IntToStr(FModule));
end;

class function TReqSignIn.ReqClass: TReqBaseClass;
begin
  Result := TReqSignIn;
end;

end.
于 2013-08-09T22:49:48.987 回答