2011-09-06 2 views
2

Delphi에서 인터페이스를 사용하고 참조 카운팅을 무시할 때 인터페이스가 참조 카운트 0에 도달하면 Delphi가 호출하는 theRelease 호출을 우회 할 수 있습니다._Release가 호출되지 않도록 인터페이스와 클래스를 혼합하는 방법은 무엇입니까?

하지만 클래스와 인터페이스를 혼합 할 때 (매우 유용함) _Release 메소드는 무엇이든지간에 항상 호출됩니다. 문제는 아래 샘플 코드에서 로컬 개체가 nill-ed이지만 _Release가 여전히 호출된다는 것입니다. 잘못된 메모리는 예외입니다. 응용 프로그램의 메모리 연산에 따라 nReasonObject의 이전 위치에서 _Release가 호출되거나 예외적으로 메모리가 다시 사용되지 않으면 예외가 발생할 수 있습니다.

그래서 컴파일러가 "제거/차단/회피/종료/리다이렉트/vmt 하이재킹/종료/마약 등"등의 호출을 생성 할 수 있습니까? 이것이 가능하다면 Delphi에서 적절한 순수 인터페이스를 사용할 수 있습니다.

unit TestInterfaces; 

interface 

uses 
    Classes, 
    SysUtils; 

type 

    ITestInterface = interface 
    ['{92D4D6E4-A67F-4DB4-96A9-9E1C40825F9C}'] 
    procedure Run; 
    end; 

    TTestClass = class(TInterfacedObject, ITestInterface) 
    protected 
    function _AddRef: Integer; stdcall; 
    function _Release: Integer; stdcall; 
    public 
    procedure Run; 
    end; 

    TRunTestClass = class(TObject) 
    protected 
    FlocalInterface : ITestInterface; 
    FlocalObject : TTestClass; 
    public 
    constructor Create; 
    destructor Destroy; override; 
    procedure Test; 
    end; 

    procedure RunTest; 
    procedure RunTestOnClass; 

var 
    globalInterface : ITestInterface; 

implementation 


procedure RunTest; 
var 
    localInterface : ITestInterface; 
    localObject : TTestClass; 
begin 

    try 

    //create an object 
    localObject := TTestClass.Create; 

    //local scope 
    // causes _Release call when object is nilled 
    localInterface := localObject; 
    localInterface.Run; 

    //or global scope 
    // causes _Release call when exe shuts down - possibly on invalid memory location 
    globalInterface := localObject; 
    globalInterface.Run; 

    finally 
    //localInterface := nil; //--> forces _Release to be called 
    FreeAndNil(localObject); 
    end; 

end; 

procedure RunTestOnClass; 
var 
    FRunTestClass : TRunTestClass; 
begin 
    FRunTestClass := TRunTestClass.Create; 
    FRunTestClass.Test; 
    FRunTestClass.Free; 
end; 


{ TTheClass } 

procedure TTestClass.Run; 
begin 
    beep; 
end; 

function TTestClass._AddRef: Integer; 
begin 
    result := -1; 
end; 

function TTestClass._Release: integer; 
begin 
    result := -1; 
end; 

{ TRunTestClass } 

constructor TRunTestClass.Create; 
begin 
    FlocalObject := TTestClass.Create; 
    FlocalInterface := FlocalObject; 
end; 

destructor TRunTestClass.Destroy; 
begin 
    //.. 
    FlocalObject.Free; 
    //FlocalObject := nil; 
    inherited; 
end; 

procedure TRunTestClass.Test; 
begin 
    FlocalInterface.Run; 
end; 

end. 

답변

3

찾고있는 것을 달성하기위한 실용적인 방법이 없습니다. 컴파일러는 _Release으로 전화를 내고 으로 전화를 걸어으로 전화를 걸면 모든 통화 사이트를 찾아야합니다. 실용적이지 않습니다.

참조 계산 수명 관리를 사용할 수없는 유일한 방법은 걱정할 필요가 없습니다. Free을 호출하기 전에 모든 인터페이스 참조를 완료 (예 : nil)해야합니다.

+0

실제로. "깔끔하게"사용하면 "무효"로 설정됩니다. _Release는 실제로 nil 인 인터페이스 참조에서 호출되지 않습니다. –

+0

무료 –

+2

을 호출하기 전에 마지막 인터페이스 참조가 객체를 해제하지 못하도록 _AddRef를 한 번 호출하면 아무 것도 도움이되지 않습니다. @Lars. AddRef 함수는 이미 참조를 계산하지 않도록 구현되었으므로 호출해도 아무런 효과가 없습니다. 피할 필요가있는 Release 호출이고 컴파일러는 현재 참조 수에 관계없이 해당 함수에 잠재적 호출을 삽입합니다. 항상 * non-nil 변수가 필요합니다. 결론은 명시 적으로 참조를 계산하지 않더라도 객체를 해제하기 전에 인터페이스 참조 수가 0인지 확인해야한다는 것입니다. –

1

인터페이스를 사용할 때 더 이상 객체를 해제 할 필요가 없습니다. 인터페이스 된 객체는 동일한 객체에 대한 참조가 없을 때 자동으로 해제됩니다.

샘플에서는 TInterfacedObject 클래스에 정의 된 TTestClass에서 _Release 및 _Addref 함수를 삭제해야합니다.

RunTest 프로 시저에서는 localInfo를 nil로 설정하여 finally 섹션에서만 localObject를 해제 할 필요가 없습니다. 프로 시저의 종료 후, localInterface는 로컬 객체를 자동적으로 파기합니다.

try 
    ... use your code 
    ... 
finnaly 
    globalInnterface := nil; 
end; 

약 TTestRun.Destroy는이 소멸자를 공백으로 남겨 둡니다. FlocalObject를 해제하면 안됩니다.

TTestRun.Destroy; 
begin 
    inherited; 
end; 
+2

이것은 질문을 언급하지 않는다 –

+0

@Mahdi David가 말한 것과 같음 – MX4399

관련 문제