[阅读: 717] 2006-02-13 14:12:18
TLjnInterfacedObject = class(TObject, IInterface)
protected
FRefCount: Integer;
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
class function NewInstance: TObject; override;
property RefCount: Integer read FRefCount;
end;
{ TLjnInterfacedObject }
function TLjnInterfacedObject._AddRef: Integer;
begin
//Result := InterlockedIncrement(FRefCount);
Result:=-1;
end;
function TLjnInterfacedObject._Release: Integer;
begin
{Result := InterlockedDecrement(FRefCount);
if Result = 0 then
Destroy;
}
Result:=-1;
end;
procedure TLjnInterfacedObject.AfterConstruction;
begin
InterlockedDecrement(FRefCount);
end;
procedure TLjnInterfacedObject.BeforeDestruction;
begin
// if RefCount <> 0 then
// System.Error(reInvalidPtr);
end;
class function TLjnInterfacedObject.NewInstance: TObject;
begin
Result := inherited NewInstance;
TLjnInterfacedObject(Result).FRefCount := 1;
end;
function TLjnInterfacedObject.QueryInterface(const IID: TGUID;
out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result := 0
else
Result := E_NOINTERFACE;
end;
上面的代码直接抄自 TInterfacedObject, 然后改动了一点。代码中注释掉的部分是TInterfacedObject 原来有的,注释部分后紧跟的代码是新加的。代码改动的意义在于,释放接口的时候,不释放实现接口的对象;
然后,定义一个自己的类:
TLjnObj=class(TLjnInterfacedObject,ILjnInterface)
public
procedure Hello(S:string);
end;
procedure TLjnObj.Hello(S: string);
begin
ShowMessage(S);
end;
作了上述改动后的类,在使用的时候:
procedure TForm1.Button1Click(Sender: TObject);
var
MyObj:TLjnObj;
MyCom:TComponent;
i:Integer;
S:string;
begin
MyObj:=TLjnObj.Create;
MyInt:=ILjnInterface(MyObj);
{
i:=MyObj.RefCount;
S:=IntToStr(i);
Memo1.Lines.Add(IntToStr(i));
} //但不能在这段代码里,读这个对象的引用计数值。能读到,不过如果读了,则在做 MyObj.Free 的时候会出现 AV错误,或者执行释放的时候不出错,但程序关闭的时候出 AV 错误。如果执行了这段代码,而又在后面执行了释放接口引用的代码 MyInt=nil,然后才释放对象 MyObj.Free 则也不会出错。
MyInt.Hello('haha');
//MyInt:=nil; //可以不释放这个接口引用,直接释放对象 MyObj 不会出 AV 错误。
{
i:=MyObj.RefCount;
S:=IntToStr(i);
Memo1.Lines.Add(IntToStr(i));
}
MyObj.Free;
end;
总结:
1. 接口引用没有释放干净,即没有做 MyInt=nil 的时候:
1.1. 如果只调用了接口方法,没有去读对象的MyObj.RefCount;则可以直接释放对象,不会出错。
1.2. 如果读了对象的接口引用计数值,则释放对象的时候会出错。
1.3. 如果读了对象的接口引用计数值,然后释放了接口 MyInt=nil 然后释放对象,则不会出错。
总结:要想没有释放接口引用就直接释放对象,对象需要改造。而且,这时候还不能读对象的接口引用计数值,否则释放的时候还是会出错。能不能对这个对象做其它操作,还需要做进一步实验。