Как смешивать интерфейсы и классы, избегая вызова _Release? - PullRequest
2 голосов
/ 06 сентября 2011

При использовании интерфейсов в Delphi и переопределении подсчета ссылок можно обойти вызовы _Release, которые Delphi выполняет, когда интерфейс достигает счетчика ссылок, равного нулю.

Но - при смешивании классов и интерфейсов (что очень полезно) метод _Release ВСЕГДА вызывается независимо от того, что.Проблема заключается в том, что в приведенном ниже примере кода локальный объект имеет нулевое значение, но _Release все еще вызывается - за исключением недействительной памяти.В зависимости от операций с памятью в приложении может возникнуть исключение, когда _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.

Ответы [ 2 ]

3 голосов
/ 06 сентября 2011

Нет практического способа достичь того, что вы ищете.Компилятор собирается отправлять вызовы на _Release, и, чтобы ударить их, вам нужно будет найти все сайты вызовов.Это не практично.

Боюсь, что единственный жизнеспособный подход, когда управление временем жизни с подсчетом ссылок отключено, - это убедиться, что вы завершили (т.е. установили nil) все ссылки на интерфейс перед вызовом Free.

1 голос
/ 06 сентября 2011

Когда вы используете интерфейсы, вам больше не нужно освобождать свои объекты. Интерфейсные объекты будут освобождены автоматически, когда нет никаких ссылок на тот же объект.

В вашем примере вы должны удалить функции _Release и _Addref в TTestClass, они определены в классе TInterfacedObject.

В процедуре RunTest вам не нужно освобождать localObject только в разделе, наконец, установите globalInterface равным nil. после завершения процедуры localInterface автоматически уничтожит локальный объект.

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

А насчет TTestRun. Дестрой только что оставил этот деструктор пустым. Вы не должны освобождать FlocalObject.

TTestRun.Destroy;
begin
  inherited;
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...