如果我理解正确,这很好:
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');
我有三个问题:
MyClass1, MyClass2
现在是对象类型,而不是简单示例中的接口类型。这样可以吗?
我应该释放()或“无”MyClass1,甚至可能不管它吗?
在处理完2.之后,仍然需要关于引用计数的两个ix:= nil
语句吗?
答案 0 :(得分: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
,您的计划将会崩溃。自己释放对象不会更改i1
或i2
中的值,因此编译器自动插入的引用计数代码仍将执行。它会尝试减少已经释放的对象的引用计数,这显然不是很好。
但是假设您等到之后,您已清除i1
和i2
,如下代码所示:
i1 := nil;
i2 := nil;
MyClass1.Free;
那还是错的。清除变量会将引用计数设置为0,因此在分配给i2
时对象将被销毁; MyClass1
中的值无效,因此您不应在其上调用Free
。
最安全的做法是,一旦为对象引用分配了对象引用,就要立即清除对象引用。那么你就不会再想要使用它了。
通常不需要清除接口变量。它在生命周期结束时自动清除(对于局部变量,它们在函数结束时超出范围)。此外,如果您调用Supports
并传入已分配的接口引用,它将接收对新对象的接口引用,或者它将被清除;它不会继续保持其先前的价值。
也就是说,当您致电Supports(MyClass2,IFirstInterface,i1);
时,无需先清除i1
。对Supports
的调用将填充i1
,引用IFirstInterface
引用MyClass2
引用的对象,或者将nil
存储在i1
中}}
答案 1 :(得分:0)
原则上......我同意每个人都在谈论混合接口和对象模型的所有内容,不要混用它们......
这是让你自己陷入困境,并对奇怪的GPF进行深夜追踪的简单方法......
但是......(总有一个但是......)
你可以做到......如果你理解了Gotcha的......
Checkout Button4Click和Button5Click ... Button4Click通过界面完成... 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.