오늘 뭔가 테스트하는 동안 이상한 상황에 처했습니다.Delphi Interface Reference Counting
많은 인터페이스와 개체가 있습니다. 코드는 다음과 같습니다
IInterfaceZ = interface(IInterface)
['{DA003999-ADA2-47ED-A1E0-2572A00B6D75}']
procedure DoSomething;
end;
IInterfaceY = interface(IInterface)
['{55BF8A92-FCE4-447D-B58B-26CD9B344EA7}']
procedure DoNothing;
end;
TObjectB = class(TInterfacedObject, IInterfaceZ)
procedure DoSomething;
end;
TObjectC = class(TInterfacedObject, IInterfaceY)
public
FTest: string;
procedure DoNothing;
end;
TObjectA = class(TInterfacedObject, IInterfaceZ, IInterfaceY)
private
FInterfaceB: IInterfaceZ;
FObjectC: TObjectC;
function GetBB: IInterfaceZ;
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
property BB: IInterfaceZ read GetBB implements IInterfaceZ;
property CC: TObjectC read FObjectC implements IInterfaceY;
end;
procedure TObjectB.DoSomething;
begin
Sleep(1000);
end;
procedure TObjectA.AfterConstruction;
begin
inherited;
FInterfaceB := TObjectB.Create;
FObjectC := TObjectC.Create;
FObjectC.FTest := 'Testing';
end;
procedure TObjectA.BeforeDestruction;
begin
FreeAndNil(FObjectC);
FInterfaceB := nil;
inherited;
end;
function TObjectA.GetBB: IInterfaceZ;
begin
Result := FInterfaceB;
end;
procedure TObjectC.DoNothing;
begin
ShowMessage(FTest);
end;
을 지금은이 같은 다양한 구현에 액세스하는 경우 나는 다음과 같은 결과를 얻을 : YY를
procedure TestInterfaces;
var
AA: TObjectA;
YY: IInterfaceY;
ZZ: IInterfaceZ;
NewYY: IInterfaceY;
begin
AA := TObjectA.Create;
// Make sure that the Supports doesn't kill the object.
// This line of code is necessary in XE2 but not in XE4
AA._AddRef;
// This will add one to the refcount for AA despite the fact
// that AA has delegated the implementation of IInterfaceY to
// to FObjectC.
Supports(AA, IInterfaceY, YY);
YY.DoNothing;
// This will add one to the refcount for FInterfaceB.
// This is also allowing a supports from a delegated interface
// to another delegated interface.
Supports(YY, IInterfaceZ, ZZ);
ZZ.DoSomething;
// This will fail because the underlying object is actually
// the object referenced by FInterfaceB.
Supports(ZZ, IInterfaceY, NewYY);
NewYY.DoNothing;
end;
첫 번째 지지대가 구현에 변수를 사용하는 호출을 반환 이것은 실제로 TObjectA에 대한 참조입니다. 내 AA 변수는 참조 횟수입니다. 기본 참조 계산 개체는 TObjectA이므로 지원 호출에서 인터페이스를 사용하는 두 번째 지원은 작동하고 인터페이스를 반환합니다. 기본 객체는 실제로 이제는 TObjectB입니다. FInterfaceB 뒤에있는 내부 객체는 참조 카운트 된 객체입니다. 이 부분은 GetBB가 실제로 FInterfaceB이기 때문에 의미가 있습니다. 여기서 예상 한대로 Supports에 대한 마지막 호출은 NewYY에 대해 null을 반환하고 마지막에 호출이 실패합니다.
내 질문은 TObjectA에 대한 참조 계산이 첫 번째 지원 호출과 디자인에 의한 것입니까? 즉, 인터페이스를 구현하는 속성이 인터페이스가 아닌 객체를 반환하면 소유자 객체가 참조 계산을 수행하는 객체가됩니다. 나는 항상 내부 객체가 주 객체 대신에 참조 카운트되는 결과를 가져올 것이라는 인상을 받았다. 상기
이 옵션property BB: IInterfaceZ read GetBB implements IInterfaceZ;
가 FInterfaceB 뒤에 내부 오브젝트 레퍼런스 카운트 하나이다 : 다음
선언이다.
property CC: TObjectC read FObjectC implements IInterfaceY;
위의 두 번째 옵션에서 TObjectA는 참조 카운트 된 것이고 위임 된 개체 FObjectC는 아닙니다. 이것은 의도적으로 설계된 것입니까?
편집
난 그냥 XE2에서이 테스트를 거쳐 동작은 다릅니다. 두 번째 Supports 문은 ZZ에 대해 nil을 반환합니다. XE4의 디버거는 YY가 (TObjectA를 IInterfaceY로 참조하고 있음을 나타냅니다. XE2에서 그것은 a (Pointer as IInterfaceY)라고 알려줍니다. 또한 XE2에서 AA는 첫 번째 지원 문에서 계수되지 않지만 내부 FObjectC는 참조 카운트됩니다. 질문 후
추가 정보
하나주의해야 할 점은이에이 대답했다. 인터페이스 버전은 체인화 할 수 있지만 객체 버전은 연결할 수 없습니다. 즉,이 같은 작동한다는 것을 의미 :
TObjectBase = class(TInterfacedObject, IMyInterface)
…
end;
TObjectA = class(TInterfacedObject, IMyInterface)
FMyInterfaceBase: IMyInterface;
property MyDelegate: IMyInterface read GetMyInterface implements IMyInterface;
end;
function TObjectA.GetMyInterface: IMyInterface;
begin
result := FMyInterfaceBase;
end;
TObjectB = class(TInterfacedObject, IMyInterface)
FMyInterfaceA: IMyInterface;
function GetMyInterface2: IMyInterface;
property MyDelegate2: IMyInterface read GetMyInterface2 implements IMyInterface;
end;
function TObjectB.GetMyInterface2: IMyInterface;
begin
result := FMyInterfaceA;
end;
그러나 오브젝트 버전이 TObjectB이 인터페이스의 메소드를 구현하지 않는다는 말과 함께 컴파일러 오류를 제공합니다.
TObjectBase = class(TInterfacedObject, IMyInterface)
…
end;
TObjectA = class(TInterfacedObject, IMyInterface)
FMyObjectBase: TMyObjectBase;
property MyDelegate: TMyObjectBase read FMyObjectBase implements IMyInterface;
end;
TObjectB = class(TInterfacedObject, IMyInterface)
FMyObjectA: TObjectA;
property MyDelegate2: TObjectA read FMyObjectA implements IMyInterface;
end;
위임을 연결하려면 인터페이스를 고수하거나 다른 방법으로 사용해야합니다.
당신은'TObjectA'에 대한 약한 참조만을 가지고 있습니다. 그러므로 당신은 AA를 잃고 나머지는 잃게 될 것입니다. –
@SirRufo 나는 그것을 이해하고있다. 나는 참조 계산이 어떻게 행해지는지에 더 관심이있다. 기본적으로 어떤 개체가 참조 카운트되고 있습니다. – Graymatter
_AddRef/_Release 메서드 호출을 무시하고 주 프로 시저뿐만 아니라 해당 호출도 기록하면 볼 수 있습니다. –