Vamos falar de alguns cuidados que temos que ter ao usar interface para não deixarmos acontecer AccessViolation em rotinas que utilizem Interface.
Imaginamos a seguinte situação. Você tem uma interface que implementa uma rotina básica para salvar informações do banco de dados. No entanto você está salvando duas classes, TNotaFiscal e TItemNF, cada uma herdando diretamente de IClasseBase(Interface que implementa a rotina de salvar). Abaixo, o código fonte da Unit com a Interface e as classes montadas.
unit UInterface;
interface
uses
Vcl.Dialogs;
type
// Interface que implementa o método Salvar que será usado nas demais classes.
IClasseBase = Interface
['{7BAE5AE6-E856-4977-B156-71614AD72528}']
procedure MetodoSalvarNaInterface;
end;
TNotaFiscal = class(TInterfacedObject, IClasseBase)
procedure MetodoSalvarNaInterface;
function MetodoDaClasseNotaFiscal: string;
end;
TItemNF = class(TInterfacedObject, IClasseBase)
procedure MetodoSalvarNaInterface;
function MetodoDaClasseItemNF: string;
end;
implementation
{ TNotaFiscal }
function TNotaFiscal.MetodoDaClasseNotaFiscal: string;
begin
Result := 'SalvarClasseNotaFiscal';
end;
procedure TNotaFiscal.MetodoSalvarNaInterface;
begin
ShowMessage(MetodoDaClasseNotaFiscal);
end;
{ TItemNF }
function TItemNF.MetodoDaClasseItemNF: string;
begin
Result := 'SalvarClasseItemNF';
end;
procedure TItemNF.MetodoSalvarNaInterface;
begin
ShowMessage(MetodoDaClasseItemNF);
end;
end.
Agora no form principal, coloque um botão e deixe a Unit com o código o mais parecido com o da listagem abaixo:unit fPrincipal;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, UInterface;
type
TfrmPrincipal = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
Nota: TNotaFiscal;
Item: TItemNF;
procedure ChamadaDeMetodoDaInterface(pClasseBase: IClasseBase);
{ Private declarations }
public
{ Public declarations }
end;
var
frmPrincipal: TfrmPrincipal;
implementation
{$R *.dfm}
procedure TfrmPrincipal.Button1Click(Sender: TObject);
begin
ChamadaDeMetodoDaInterface(Nota);
ChamadaDeMetodoDaInterface(Item);
end;
procedure TfrmPrincipal.ChamadaDeMetodoDaInterface(
pClasseBase: IClasseBase);
begin
pClasseBase.MetodoEspecificoInterface;
end;
procedure TfrmPrincipal.FormCreate(Sender: TObject);
begin
Nota := TNotaFiscal.Create;
Item := TItemNF.Create;
end;
end.
Execute a aplicação, clique no botão, verifique as duas mensagens. Clique no botão novamente: BINGO!!!!. Um AccessViolation ocorreu. Correto?!
Entendendo o problema:
Um dos conceitos de interfaces é garantir o gerenciamento de memória dos objetos que os implementam. Os métodos _AddRef e _Release implementam esse gerenciamento, incrementam a contagem de referência sobre o objeto e destroem o mesmo quando o contador de referência é zero.
O erro ocorre porque ao final da execução do método a Interface automaticamente zera esse contador destruindo a referência sobre o objeto criado(somente a referência), no caso TNotaFiscal e TItemNF, e quando executamos novamente a referência não existe mais e então ocorre o AccessVIolation.
Contornando o problema de duas formas diferentes:
Exemplo 1:
Veja a rotina abaixo:
procedure ChamadaDeMetodoDaInterface(pClasseBase: IClasseBase);Utilizando a reservada const na declaração do método, contornamos esse problema
procedure ChamadaDeMetodoDaInterface(const pClasseBase: IClasseBase);
O que é isso?
Quando passamos o parâmetro com a reservada const, o mesmo é passado por valor e não por referência, o que tira das mãos da Interface a destruição da referência deixando por conta do usuário implementar a rotina de liberação como na rotina abaixo:
procedure TfrmPrincipal.FormDestroy(Sender: TObject); begin Nota.Free; end;Exemplo 2
Para não precisarmos nos preocupar com o const nem com o Free, declaramos as variáveis com o tipo da nossa Interface: IClasseBase. Com isso, deixamos a Interface gerenciar a destruição da referência e dos objetos relacionados. Lembrando que a criação das variáveis deve continuar sendo dos seus tipos específicos, ou seja:
Nota := TNotaFiscal.Create; Item := TItemNF.Create;
Era isso o que tínhamos para o momento. Abraço a todos, dúvidas no final do post e até a próxima.
Nenhum comentário :
Postar um comentário