terça-feira, 12 de maio de 2009

Crie seu próprio Object Inspector!


Antes de mais nada, é bom dizer que esse não é um object inspector para ser usado em Design - Time,
mas sim para ser usado em runtime. Ou seja, não foi usado nada de OTA. O objetivo principal desse
object inspector é permitir que o usuario modifique propriedades dos componentes do seu programa ao gosto
dele e salvar isso no banco de dados.

Nada impede de você usar OTA e transforma-lo em um Object Inspector turbinado para design.
Outro objetivo interessante seria listar os nomes dos metodos da form que são eventos,
para que o evento de um componente possa ser trocado por outro, dando ao usuario final até
uma certa liberdade de "Programação".

Com essa técnica você pode criar, em seu aplicativo, um gerador de telas/cadastros do
usuário, onde você disponibiliza uma paleta de componentes como a do delphi e um object
inspector para listar e modificar as propriedades. As propriedades podem ser armazenadas em XML ou banco de dados.

Esse Object Inspector está longe de ser completo, obviamente, mas espero que ele sirva
de base para quem desejar criar uma IDE de configuração para o usuario, igual ao editor
do RAVE.
Em primeiro lugar, note a unit TypInfo. Ela é muito util mesmo ;)
Repare também nos métodos: GetPropList, GetPropInfo, GetMethodProp, IsPublishedProp e
o método MethodName da classe TObject.

Atualmente temos manipuladores de cores, fontes e strings ou inteiros.
Ainda falta construir manipuladores de sets, enums, objetos e eventos.
Mas já dá para ter um vislumbre do que é possivel fazer com ele.

Teste com um componente de cada vez, primeiro com um edit de uma form de teste.
Abra-o conforme o exemplo em anexo, mude a cor desse edit, salve o xml dele,
feche o programa, abra o mesmo edit e carregue o xml dele.
Verá que a cor do edit mudou. O mesmo dá pra fazer com qualqier outra propriedade.


unit uFrmVtrPropertyInspector;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, typinfo, StdCtrls, DB, DBClient, Grids, DBGrids, ComCtrls;

type
  TfrmVtrPropertyInspector = class(TForm)
    cdsProps: TClientDataSet;
    dsProps: TDataSource;
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    TabSheet2: TTabSheet;
    dbgProps: TDBGrid;
    TabSheet3: TTabSheet;
    Memo1: TMemo;
    dbgEvts: TDBGrid;
    cdsEvts: TClientDataSet;
    dsevts: TDataSource;
    cbObjetos: TComboBox;
    TabSheet4: TTabSheet;
    mInfo: TMemo;
    btSalvar: TButton;
    btCarregar: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure cbObjetosChange(Sender: TObject);
    procedure dbgPropsEditButtonClick(Sender: TObject);
    procedure btSalvarClick(Sender: TObject);
    procedure btCarregarClick(Sender: TObject);
  private
    FObjeto: TObject;
    //esse campo armazena  o owner do componente a ser analisado
    FOwnerObjetos: Tcomponent;
    //adiciona um registro de propriedade no cds
    procedure AdicionaPropriedade(iPropIdx: integer; sPropNome: string;
      sPropValor: string; sPropTipo: string);
    //adiciona um registro de evento no cds
    procedure AdicionaEvento(iPropIdx: integer; mMetodo: TMethod; PropInfo: PPropInfo; obj: Tobject);
    //seta a propriedade de um componente
    procedure SetaPropriedade(obj: Tobject; sProp: string; vValor: variant;
      Objeto: TObject = nil);
    function GetOwnerObjetos: TComponent;
  public
    procedure SetOwnerObjetos(aOwner: TComponent);
    procedure SetaObjetoAlvo(aNome: string);
    procedure PegaPropriedades(obj: Tobject);
    procedure SetaPropriedades(obj: TObject);
    property OwnerObjetos: TComponent read GetOwnerObjetos write  SetOwnerObjetos;
    procedure EditaPropriedades;
  end;

const
  STR_INDICE = 'Indice';
  STR_PROPRIEDADE = 'Propriedade';
  STR_VALOR = 'Valor';
  STR_TIPO = 'Tipo';
  STR_CODIGO = 'Codigo';
  STR_DADOS = 'Dados';
  STR_COMPONENTE = 'ONDE';

implementation

{$R *.dfm}

{ TForm1 }

procedure TfrmVtrPropertyInspector.PegaPropriedades(obj: Tobject);
var
  lista: PPropList;
  i: integer;
  iNumProps: integer;
  sNomeProp: string;
  mMetodo: TMethod;
  pPropriedade: PPropInfo;
begin

  FObjeto := obj;

  cdsProps.EmptyDataSet;

  iNumProps := GetPropList(obj, lista);
       
  if  (obj is TComponent)  then
  begin
    SetOwnerObjetos((obj as TComponent).Owner);
    self.Caption := 'Vitor Rubio''s Property Inspector' +
      ' ---> ' + (obj as TComponent).Name + ' Possui ' +
      inttostr(iNumProps)+ ' Propriedades:';
    SetaObjetoAlvo((obj as TComponent).Name);
  end
  else
    self.Caption := 'Vitor Rubio''s Property Inspector' +
      ' ---> ' + obj.ClassName + ' Possui ' +
      inttostr(iNumProps)+ ' Propriedades:';

  for i:= low(lista^) to iNumProps-1 do
  try
    sNomeProp := '';
    if (lista^[i] <> nil) then
    begin
      sNomeProp := lista^[i].Name;
      pPropriedade := GetPropInfo(obj, sNomeProp);

      if (pPropriedade.PropType^.Kind = tkMethod) then
      begin
        mMetodo := GetMethodProp(obj, sNomeProp);
        AdicionaEvento(i, mMetodo, pPropriedade, obj);
      end
      else
        AdicionaPropriedade(i, sNomeProp, string(GetPropValue(obj, sNomeProp)),
          lista^[i].PropType^^.Name);
    end;

  except
    if sNomeProp = '' then
      sNomeProp := 'Erro';
    AdicionaPropriedade(i, sNomeProp, 'Erro', '');
  end;

end;

procedure TfrmVtrPropertyInspector.AdicionaPropriedade(iPropIdx: integer; sPropNome: string;
  sPropValor: string; sPropTipo: string);
begin
  cdsProps.Insert;
  cdsProps.FieldByName(STR_INDICE).AsInteger := iPropIdx;
  cdsProps.FieldByName(STR_PROPRIEDADE).AsString := sPropNome;
  cdsProps.FieldByName(STR_VALOR).AsString := sPropValor;

  cdsProps.FieldByName(STR_TIPO).AsString := sPropTipo;
  cdsProps.Post;
end;

procedure TfrmVtrPropertyInspector.FormCreate(Sender: TObject);
var
  ColProps, Colevts: integer;
begin
    //propriedades
    with cdsProps.FieldDefs.AddFieldDef do
    begin
      Name := STR_INDICE;
      DataType := ftInteger;
    end;

    with cdsProps.FieldDefs.AddFieldDef do
    begin
      Name := STR_TIPO;
      DataType := ftString;
    end;

    with cdsProps.FieldDefs.AddFieldDef do
    begin
      Name := STR_PROPRIEDADE;
      DataType := ftString;
    end;

    with cdsProps.FieldDefs.AddFieldDef do
    begin
      Name := STR_VALOR;
      DataType := ftString;
      Size := 255;
    end;

    with cdsProps.IndexDefs.AddIndexDef do
    begin
      Fields := STR_INDICE;
    end;
    cdsProps.IndexFieldNames := STR_INDICE;
    cdsProps.CreateDataSet;
    cdsProps.Open;

    for ColProps := 0 to dbgProps.Columns.Count-1 do
    begin
      if dbgProps.Columns[ColProps].Title.Caption = 'Valor' then
        dbgProps.Columns[ColProps].ButtonStyle := cbsEllipsis;
    end;
    //fim propriedades

    //eventos
    with cdsEvts.FieldDefs.AddFieldDef do
    begin
      Name := STR_INDICE;
      DataType := ftInteger;
    end;

    with cdsEvts.FieldDefs.AddFieldDef do
    begin
      Name := STR_TIPO;
      DataType := ftString;
    end;

    with cdsEvts.FieldDefs.AddFieldDef do
    begin
      Name := STR_PROPRIEDADE;
      DataType := ftString;
    end;
    
    with cdsEvts.FieldDefs.AddFieldDef do
    begin
      Name := STR_CODIGO;
      DataType := ftString;
    end;

    with cdsEvts.FieldDefs.AddFieldDef do
    begin
      Name := STR_DADOS;
      DataType := ftString;
    end;

    with cdsEvts.FieldDefs.AddFieldDef do
    begin
      Name := STR_COMPONENTE;
      DataType := ftString;
    end;

    with cdsEvts.FieldDefs.AddFieldDef do
    begin
      Name := STR_VALOR;
      DataType := ftString;
    end;

    with cdsEvts.IndexDefs.AddIndexDef do
    begin
      Fields := STR_INDICE;
    end;
    cdsEvts.IndexFieldNames := STR_INDICE;
    cdsEvts.CreateDataSet;
    cdsEvts.Open;
    //fim eventos

end;

procedure TfrmVtrPropertyInspector.SetaPropriedades(obj: TObject);
begin
  cdsProps.First;
  while not cdsProps.Eof do
  try
    try
      SetaPropriedade(obj, cdsProps.fieldbyname(STR_PROPRIEDADE).AsString,
        cdsProps.fieldbyname(STR_VALOR).AsVariant);
    except
    end;
  finally
    cdsProps.Next;
  end;
end;

procedure TfrmVtrPropertyInspector.AdicionaEvento(iPropIdx: integer;
  mMetodo: TMethod; PropInfo: PPropInfo; obj: Tobject);
begin
  cdsEvts.Insert;
  cdsEvts.FieldByName(STR_INDICE).AsInteger := iPropIdx;
  cdsEvts.FieldByName(STR_PROPRIEDADE).AsString := PropInfo.Name;
  if mMetodo.Data <> nil then
    cdsEvts.FieldByName(STR_COMPONENTE).AsString := TComponent(mMetodo.Data).Name;
  if mMetodo.Code <> nil then
    cdsEvts.FieldByName(STR_VALOR).AsString := FOwnerObjetos.MethodName(mMetodo.Code);
  cdsEvts.FieldByName(STR_CODIGO).AsString := IntToHex(Integer(GetMethodProp(obj, PropInfo.Name).Code), 10);
  cdsEvts.FieldByName(STR_DADOS).AsString := IntToHex(Integer(GetMethodProp(obj, PropInfo.Name).Data), 10);
  cdsEvts.FieldByName(STR_TIPO).AsString := PropInfo.PropType^.Name;
  cdsEvts.Post;
end;

procedure TfrmVtrPropertyInspector.SetaPropriedade(obj: Tobject;
  sProp: string; vValor: variant; Objeto: TObject);
var
  PropInfo: PPropInfo;
  TypeData: PTypeData;
  DynArray: Pointer;
  Instance: TObject;
  PropName: string;
  Value: Variant;

  function RangedValue(const AMin, AMax: Int64): Int64;
  begin
    Result := Trunc(Value);
    if (Result < AMin) or (Result > AMax) then
      raise Exception.Create('Valor fora da faixa permitida');
  end;

begin

  Instance:= obj;
  PropName:= sProp;
  Value:= vValor;

  // get the prop info
  PropInfo := GetPropInfo(Instance, PropName);
  if PropInfo = nil then
    raise Exception.Create('Propriedade não encontrada: ' + sProp)
  else
  begin
    TypeData := GetTypeData(PropInfo^.PropType^);

    // set the right type
    case PropInfo.PropType^^.Kind of
      tkInteger, tkChar, tkWChar:
        if TypeData^.MinValue < TypeData^.MaxValue then
          SetOrdProp(Instance, PropInfo, RangedValue(TypeData^.MinValue,
            TypeData^.MaxValue))
        else
          // Unsigned type
          SetOrdProp(Instance, PropInfo,
            RangedValue(LongWord(TypeData^.MinValue),
            LongWord(TypeData^.MaxValue)));
      tkEnumeration:
        if VarType(Value) = varString then
          SetEnumProp(Instance, PropInfo, VarToStr(Value))
        else if VarType(Value) = varBoolean then
          // Need to map variant boolean values -1,0 to 1,0
          SetOrdProp(Instance, PropInfo, Abs(Trunc(Value)))
        else
          SetOrdProp(Instance, PropInfo, RangedValue(TypeData^.MinValue,
            TypeData^.MaxValue));
      tkSet:
        if VarType(Value) = varInteger then
          SetOrdProp(Instance, PropInfo, Value)
        else
          SetSetProp(Instance, PropInfo, VarToStr(Value));
      tkFloat:
        SetFloatProp(Instance, PropInfo, Value);
      tkString, tkLString:
        SetStrProp(Instance, PropInfo, VarToStr(Value));
      tkWString:
        SetWideStrProp(Instance, PropInfo, VarToWideStr(Value));
      tkVariant:
        SetVariantProp(Instance, PropInfo, Value);
      tkInt64:
        SetInt64Prop(Instance, PropInfo, RangedValue(TypeData^.MinInt64Value,
          TypeData^.MaxInt64Value));
   tkDynArray:
  begin
    DynArrayFromVariant(DynArray, Value, PropInfo^.PropType^);
    SetOrdProp(Instance, PropInfo, Integer(DynArray));
  end;
    tkClass:
      begin
        //if Objeto <> nil then
        //  SetObjectProp(Instance, PropInfo, Objeto);
        //SetOrdProp(Instance, PropInfo, vValor);

        {cdsProps.FieldByName(STR_VALOR).AsString :=
          string(GetPropValue(obj,
          cdsProps.FieldByName(STR_PROPRIEDADE).AsString)); }
      end;

    else
      raise Exception.Create('Tipo invalido de propriedade.');
    end;
  end;

end;

procedure TfrmVtrPropertyInspector.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  Release;
end;

procedure TfrmVtrPropertyInspector.SetOwnerObjetos(aOwner: TComponent);
var
  i: integer;
  TipData: PTypeData;
begin
  FOwnerObjetos := aOwner;
  TipData :=  GetTypeData(FOwnerObjetos.ClassInfo);

  mInfo.Lines.Add('Classe: ' + TipData.ClassType.ClassName);
  mInfo.Lines.Add('Classe Pai: ' + TipData.ParentInfo^.Name);
  mInfo.Lines.Add('Nº de propriedades: ' + inttostr(TipData.PropCount));
  mInfo.Lines.Add('Nome da Unit: ' + TipData.UnitName);

  for i := 0 to aOwner.ComponentCount -1 do
  begin
    cbObjetos.AddItem(aOwner.Components[i].Name, aOwner.Components[i]);
  end;
end;

procedure TfrmVtrPropertyInspector.cbObjetosChange(Sender: TObject);
begin
  PegaPropriedades(cbObjetos.Items.Objects[cbObjetos.ItemIndex]);
end;

procedure TfrmVtrPropertyInspector.SetaObjetoAlvo(aNome: string);
begin
  cbObjetos.ItemIndex := cbObjetos.Items.IndexOf(aNome);
end;

procedure TfrmVtrPropertyInspector.EditaPropriedades;
var
  sNomePropriedade: string;
  sValor: variant;
  oObjeto: TObject;
  PropInfo: PPropInfo;
  oObjetinho: Tobject;
begin
  cdsProps.Edit;

  sValor := cdsProps.FieldByName(STR_VALOR).AsVariant;
  sNomePropriedade := cdsProps.FieldByName(STR_PROPRIEDADE).AsString;

  oObjeto := FObjeto;
  PropInfo := GetPropInfo(oObjeto, sNomePropriedade);

  if (cdsProps.FieldByName(STR_TIPO).AsString = 'TFont') then
  begin
    with TFontDialog.Create(nil) do
    try
      Font := TFont(integer(sValor));

      if Execute then
      begin
        sValor := integer(Font);
        SetaPropriedade(oObjeto,
          sNomePropriedade,
          sValor);
      end;
    finally
      free;
    end;
  end
  else

  if (cdsProps.FieldByName(STR_TIPO).AsString = 'TColor') then
  begin
    with TColorDialog.Create(nil) do
    try
      Color := Tcolor(sValor);
      if Execute then
      begin
        sValor := integer(Color);
        SetaPropriedade(oObjeto,
          sNomePropriedade,
          sValor);
      end;
    finally
      free;
    end;
  end
  else
  if (PropInfo.PropType^.Kind = tkClass) then
  begin
    with TfrmVtrPropertyInspector.Create(nil) do
    try
      oObjetinho :=   GetObjectProp(oObjeto,  sNomePropriedade);
      if oObjetinho <> nil then
      begin
        PegaPropriedades(oObjetinho);
        ShowModal;
      end;
    finally
      free;
    end;
  end
  else
  begin
    SetaPropriedade(oObjeto,
          sNomePropriedade,
          sValor);
    ShowMessage('Propriedade: ' + sNomePropriedade + ' Alterada para: ' + VarToStr(sValor));
  end;
  cdsProps.FieldByName(STR_VALOR).AsVariant := sValor;
  cdsProps.Post;
end;

procedure TfrmVtrPropertyInspector.dbgPropsEditButtonClick(
  Sender: TObject);
begin
  EditaPropriedades;
end;

function TfrmVtrPropertyInspector.GetOwnerObjetos: TComponent;
begin
  Result := FOwnerObjetos;
end;

procedure TfrmVtrPropertyInspector.btSalvarClick(Sender: TObject);
begin
  with TSaveDialog.Create(nil) do
  try
    if Execute then
      cdsProps.SaveToFile(FileName, dfXMLUTF8);
  finally
    Free;
  end;
end;

procedure TfrmVtrPropertyInspector.btCarregarClick(Sender: TObject);
begin
  with TOpenDialog.Create(nil)do
  try
    if Execute then
    begin
      cdsProps.EmptyDataSet;
      cdsProps.LoadFromFile(FileName);
      cdsProps.First;
      while not cdsProps.Eof do
      try
        try
            SetaPropriedade(
              FOwnerObjetos.FindComponent(cbObjetos.Text),
              cdsProps.fieldbyname(STR_PROPRIEDADE).AsString,
              cdsProps.fieldbyname(STR_VALOR).AsString
            );
        except
          //on e:Exception do
          //begin
          //  ShowMessage(e.Message);
          //end;
        end;
      finally
        cdsProps.Next;
      end;
    end;
  finally
    Free;
  end;
end;

end.
    



Abaixo o código da form que chama o inspector:


unit uFrmTeste;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Menus, TypInfo, DB, DBClient;

type
  TfrmTeste = class(TForm)
    edTeste: TEdit;
    btPegar: TButton;
    btEvt1: TButton;
    btEvt2: TButton;
    Label1: TLabel;
    Label2: TLabel;
    PopupMenu1: TPopupMenu;
    Popupdetestedestecomponente1: TMenuItem;
    PopupMenu2: TPopupMenu;
    Outropopup1: TMenuItem;
    ComboBox1: TComboBox;
    procedure btEvt1Click(Sender: TObject);
    procedure btEvt2Click(Sender: TObject);
    procedure btPegarClick(Sender: TObject);
    procedure edTesteClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmTeste: TfrmTeste;

implementation

uses uFrmVtrPropertyInspector;

{$R *.dfm}

procedure TfrmTeste.btEvt1Click(Sender: TObject);
begin
  ShowMessage('Eu sou o evento disparado pelo botão 1');
end;

procedure TfrmTeste.btEvt2Click(Sender: TObject);
begin
  ShowMessage('Eu sou o evento disparado pelo botão 2');
end;

procedure TfrmTeste.btPegarClick(Sender: TObject);
begin
  with TfrmVtrPropertyInspector.Create(nil) do
  try
    try
      PegaPropriedades(ComboBox1.Items.Objects[ComboBox1.ItemIndex]);
      Showmodal;
    except
    end;
  finally
    release;
  end;
end;

procedure TfrmTeste.edTesteClick(Sender: TObject);
begin
  (Sender as  tedit).Clear;
end;

procedure TfrmTeste.FormCreate(Sender: TObject);
var i: integer;
begin
  for i := 0 to ComponentCount-1 do
    ComboBox1.Items.AddObject(Self.Components[i].Name, Self.Components[i]);

  ComboBox1.ItemIndex := 0;
end;

end.
    



faça o download no meu skydrive clicando aqui:

segunda-feira, 4 de maio de 2009

Memory Leaks, Interfaces, Agregates e RegisterClass

    Memory Leaks, Interfaces, Agregates e RegisterClassComo criar um objeto sem saber a classe, sabendo apenas o nome daclasse como string.
    Criar objetos dinâmicos com classevariável, onde a classe pode vir de um banco de dados ou arquivode configuração.
    E como fazer para esses objetos se auto - destruiremsem causar memory leaks. Como reduzir o acoplamento em ambientesnão OO altamente acoplados.
    Nesta dica vamos ver 4 assuntos distintos, poremcorrelacionados:
        1) Interfaces e comousá-las evitando memory leaks
        2) O tipo TClass e seussemelhantes, o que são e para que servem
        3) Como instanciar e manipularobjetos dos quais você não sabe a classe - Isto envolveregistrar a classe com RegisterClass e Acha-la com FindClass
        4) A maneira certa de se usarAgregates, delegates etc sem causar memory leak.


Vamos falar agora sobre Interfaces.

    Primeiro de tudo, até hoje, o melhor materialque eu já vi sobre interfaces no Delphi é este aqui: http://edn.embarcadero.com/article/30125

    Interfaces são definidas como um“contrato” entre duas partes, um padrão de encaixe.Por exemplo, uma placa de vídeo para se conectar numa placamãe com slot pciXpress deve seguir esse padrão depinagem.
    Na prática, interfaces são comoclasses (só parecem, pelo amor de Deus), porem todos osmétodos são públicos e ela não temimplementação.
    Na verdade, todas as classes tem uma interfaceimplícita, que é o conjunto de métodospúblicos da mesma. Se uma classe tem o métodopúblico “function     Mostrar(msg:string)” então esse método faz parte da interfacedessa classe mesmo que ela não implemente nenhuma.
    Usando interfaces nós podemos intercambiarobjetos que implementam a mesma interface, mesmo que sejam de linhagensdiferentes.
    Por exemplo, se duas classes totalmente diferentes(duas forms, para exemplificar), implementam a mesma interface, masnão são irmãs, nem mãe-filha e nãotem nenhum gral de parentesco, uma variável do tipo dessainterface pode conter instancias tanto de uma form como de outra.
    Isso é essencial quando precisamos instanciare abrir uma form, mas não sabemos a princípio qual otipo, porque este vai ser definido em runtime. Então um factorymethod ou um abstract factory poderia instanciar essa form paranós e ela poderia ser “acondicionada” em umavariável do tipo dessa interface.
    Por exemplo, imagine uma interface IProcura:

IProcura = interface
['{05A634F2-B8CD-4DFD-8447-59B77DE7682F}']
    Procedure Procura(valor: variant); 
End;
    Agora imagine que você tem umformulário de procura diferente para cada form do seu projeto:ProduraCliente, ProcuraFornecedor, ProcuraProduto etc... Se todas essasforms, embora diferentes entre si, implementassem a interface IProcurae o método Procura, qualquer uma delas poderia ser instanciadanuma variável:
Var Proc: IProcura;
Então estariam corretos:
 Proc:= TProcuraCliente.create(nil);
 Proc:= TProcuraFornecedor.create(nil);
 Proc:= TProcuraProduto.create(nil);
    Interfaces também podem suprir a necessidadede herança múltipla. Mas não queremos nos delongarna questão das interfaces. Então sugiro a leitura daClube Delphi 74 e 75, e estudar livros e sites de POO a respeito.
    Um fato curioso é que na revista clube delphi74 diz que você não precisa dar um free num objeto queimplementa uma interface (se você instanciá-lo navariável de interface, claro), pois a interface éliberada da memória automaticamente.
    Isso é verdade SE E SOMENTE SE a sua classefor descendente de TInterfacedObject. Isso porque essas classesimplementam a interface básica IInterface, cujos métodossão:
 function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
 function _AddRef: Integer; stdcall;
 function _Release: Integer; stdcall;  
    Estas classes guardam uma contagem dereferências à uma instância da interface, e ométodo _Release verifica se a contagem de referênciasatinge zero.
    Se atingir zero ele dá o famoso e conhecidoDestroy. Definimos como referências o numero de usos oumenções a uma instancia de um objeto que implementa umainterface na memória.
    Ou seja, o número de variáveis queapontam para ele. Por exemplo, se uma variável aponta para umainstância de um objeto, temos uma referência. Se duasvariáveis e um parâmetro de método por valorapontam para o mesmo objeto, temos 3 referências embora o objetoseja o mesmo.
    Se damos o comando Proc:=TProcuraCliente.create(nil); e depois Proc:=TProcuraCliente.create(nil); novamente, o primeiro objeto instanciadoperde sua referência, porque não tem ninguém maisapontando para ele, uma vez que o segundo objeto sobrescreveu avariavel, que agora aponta para o segundo.
    Cada vez que um objeto que implementa uma interfaceé De-referênciado, ou seja perde a referência,é executado o método _release. (se atribuir nil a umavariável interface, por exemplo, ela vai apontar para umendereço nulo de memória e não mais para ainstância do objeto, que chamará o método _release).
    Veja a implementação de _release nodelphi 7:
    TInterfacedObject:
    function TInterfacedObject._Release: Integer;
    begin
Result := InterlockedDecrement(FRefCount); //decrementa de maneira thread-safe
if Result = 0 then 
      Destroy; //manda bala no objeto
    end;
    Mas porque eu disse que isso só ocorre SE ESOMENTE SE a sua classe for descendente de TInterfacedObject? Éporque essa classe dá um free quando FrefCount chega a zero, masa classe TInterfacedPersistent não, veja suaimplementação:
    function TInterfacedPersistent._Release: Integer;
    begin
if FOwnerInterface <> nil then
      Result := FOwnerInterface._Release
 else     
  Result := -1;
    end;  
    Na classe Tcomponent, que laaaaaa no fundo herda deTpersistent também há uma implementação de_Release, pois TComponent implementa IInterface, mas tambémnão dá o free:

    function TComponent._Release: Integer;
    begin
if FVCLComObject = nil then
      Result := -1 // -1 indicates no reference counting is taking place
else
      Result := IVCLComObject(FVCLComObject)._Release;

    end;
 
    Está Errado? Não sei dizer seestá errado, (eu nunca vi uma situação em queesses métodos retornassem algo diferente de -1, nem oFOwnerInterface ou o FVCLComObject <. de nil) mas se você usaro fastmm4 verá que há um memory leak se vocêinstanciar objetos dessas classes em uma interface e nãodestruí-los.     Já os objetos de classesderivadas de TInterfacedObject você não precisa destruir.
    Faça o teste: baixe o FastMM4, ajuste asopções de Report de Memory Leak e inclua a unit Fastmm4como primeira unit do seu DPR e sete as variáveis:
 FullDebugModeScanMemoryPoolBeforeEveryOperation := True;
 SuppressMessageBoxes:=False;
Logo depois do begin do seu DPR, ficando assim:
program
Project1;

uses
    FastMM4,
    Forms,
    Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin 
 FullDebugModeScanMemoryPoolBeforeEveryOperation := True;
     SuppressMessageBoxes:=False;
     Application.Initialize;
     Application.CreateForm(TForm1,Form1);
     Application.Run;
end.
    Eu criei a interface Iteste e 3 classes que aimplementam, uma filha de TInterfacedObject, uma filha deTinterfacedPersistent e outra filha de TComponent:
ITeste = interface(IInterface)
['{B0653AC1-B7A2-4E41-9DA3-B8E5C3480AE7}']
    procedure Testar;
end;

TClassTeste = class(TInterfacedObject, ITeste)
public
    procedure Testar;
end;

TPersistentTeste = class(TInterfacedPersistent, Iteste)
public
    procedure Testar;
end;

TComponentTeste = class(TComponent, Iteste)
public
    procedure Testar;
end;
e criei 3 botões, um para instanciar cada uma delas e executar ométodo Testar;
{ TClassTeste }
procedure TClassTeste.Testar;
begin
    ShowMessage('Testando Classe teste');
end;

{ TPersistentTeste }
procedure TPersistentTeste.Testar;
begin
    ShowMessage('Testando Persistent teste');
end;

{ TComponentTeste }
procedure TComponentTeste.Testar;
begin
    ShowMessage('Testando Componente teste');
end;

procedure TForm1.btInterfaceObjectClick(Sender: TObject);
var
    teste: ITeste;
begin
    teste := TClassTeste.create;
    teste.Testar;
end;

procedure TForm1.btInterfacePersistentClick(Sender: TObject);
var
    teste: ITeste;
begin
    teste := TPersistentTeste.create;
    teste.Testar;
end;

procedure
TForm1.btComponentClick(Sender: TObject);
var
    teste: ITeste;
begin
    teste := TComponentTeste.create(nil);
    teste.Testar;
end;
    Clique no primeiro botão e feche o programa.Reparou que não teve memory leak? Isso porque o Objeto da classeTClassTeste, ao perder sua referência no fechamento do programa,chama _release e dá um Destroy em si mesmo, visto que a contagemde referências atingiu zero. Porem, se você repetir o mesmoteste com os outros botões verá que TPersistent,TInterfacedPersistent e TComponent causam memory leaks.
    Se sua classe é filha ou de alguma formadescendente de TComponent, Tpersistent ou TInterfacedPersistent comosolucionar esse problema?
    Simples: implemente e sobrecarregue esses doismétodos:
    function _AddRef: Integer; stdcall; 
    function _Release: Integer; stdcall;
de IInterface seguindo o exemplo da classe TInterfacedObject.

    O Código da nossa unit até aqui:
unit Unit1;
interface
uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type
 TForm1 = class(TForm)
     btInterfaceObject: TButton;
     btInterfacePersistent: TButton;
     btComponent: TButton;
     procedure btInterfaceObjectClick(Sender: TObject);
     procedure btInterfacePersistentClick(Sender: TObject);
     procedure btComponentClick(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

ITeste = interface(IInterface)
['{B0653AC1-B7A2-4E41-9DA3-B8E5C3480AE7}']
    procedure Testar;

end;

TClassTeste = class(TInterfacedObject, ITeste)
public
    procedure Testar;
end;

TPersistentTeste = class(TInterfacedPersistent, Iteste)
public
    procedure Testar;
end;

TComponentTeste = class(TComponent, Iteste)
public
    procedure Testar;
end;

var

Form1: TForm1;

implementation

{$R *.dfm}

{TClassTeste }

procedure TClassTeste.Testar;
begin
    ShowMessage('Testando Classe teste');
end;

{TPersistentTeste }

procedure TPersistentTeste.Testar;
begin
    ShowMessage('Testando Persistent teste');
end;


{TComponentTeste }

procedure TComponentTeste.Testar;
begin
    ShowMessage('Testando Componente teste');
end;


procedure TForm1.btInterfaceObjectClick(Sender: TObject);
var
    teste: ITeste;
begin
    teste := TClassTeste.create;
    teste.Testar;
end;


procedure TForm1.btInterfacePersistentClick(Sender: TObject);
var
    teste: ITeste;
begin
    teste := TPersistentTeste.create;
    teste.Testar;
end;


procedure TForm1.btComponentClick(Sender: TObject);
var
 teste: ITeste;
begin
 teste := TComponentTeste.create(nil);
 teste.Testar;
end;

end.
O DFM:

object Form1: TForm1
    Left = 419
    Top = 318
    Width = 142
    Height = 151
    Caption = 'Form1'
    Color = clBtnFace
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'MS Sans Serif'
    Font.Style = []
    OldCreateOrder = False
    PixelsPerInch = 96
    TextHeight = 13
    object btInterfaceObject: TButton
 Left = 8
 Top = 16
 Width = 121
 Height = 25
 Caption = 'TInterfacedObject'
 TabOrder = 0
 OnClick = btInterfaceObjectClick
    end

    object btInterfacePersistent: TButton
     Left = 8
     Top = 48
 Width = 121
     Height = 25
     Caption = 'TInterfacedPersistent'
     TabOrder = 1
     OnClick = btInterfacePersistentClick
    end

    object btComponent: TButton
     Left = 8
     Top = 80
     Width = 121
     Height = 25
     Caption = 'TComponent'
     TabOrder = 2
     OnClick = btComponentClick
    end
end
Agora vamos sobrecarregar os métodos

    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
das nossas classe filhas de TComponent eTInterfacedPersistent:

    Você precisará também deimplementar um FRefCount igual ao TInterfacedObject.
    Mas como fazer isso sem alterar o result e seguindoo exemplo de TInterfacedObject?
    Simples Assim:
    O código final do teste, com classes filhasde TInterfacedPersistent e TInterfacedObject vai abaixo, criei umafunção chamada showmessage que escreve as mensagens nummemo, ao invez de mostrar messageboxes. Melhor para testar:
unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
  TForm1 = class(TForm)
    btInterfaceObject: TButton;
    btInterfacePersistent: TButton;
    btComponent: TButton;
    btDelegaObj: TButton;
    Memo1: TMemo;
    procedure btInterfaceObjectClick(Sender: TObject);
    procedure btInterfacePersistentClick(Sender: TObject);
    procedure btComponentClick(Sender: TObject);
    procedure btDelegaObjClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  ITeste = interface(IInterface)
  ['{B0653AC1-B7A2-4E41-9DA3-B8E5C3480AE7}']
    procedure Testar;
  end;

  TClassTeste = class(TInterfacedObject, ITeste)
  public
    procedure Testar;
  end;

  TPersistentTeste = class(TInterfacedPersistent, Iteste)
  private
    FRefCount: Integer;
  public
    procedure Testar;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;

  TComponentTeste = class(TComponent, Iteste)
  private
    FRefCount: Integer;
  public
    procedure Testar;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;

  TClasseAgregada = class(TAggregatedObject, ITeste)
  private
    FITeste: ITeste;
    //FITeste: TClassTeste;
    //FITeste: TPersistentTeste;
    //FITeste: TComponentTeste;
  public
    procedure Testar;
    constructor Create;
    property Teste: ITeste read FITeste write FITeste implements Iteste;
  end;

var
  Form1: TForm1;

  //apenas para sobrescrever a original, melhor mostrar num memo do que um monte de janelinhas
  procedure ShowMessage(msg: string);
implementation

  procedure ShowMessage(msg: string);
  begin
    Form1.Memo1.Lines.Add(msg);
  end;

{$R *.dfm}

{ TClassTeste }

procedure TClassTeste.Testar;
begin
  ShowMessage('Testando Classe teste');
end;

{ TPersistentTeste }
procedure TPersistentTeste.Testar;
begin
  ShowMessage('Testando Persistent teste');
end;

function TPersistentTeste._AddRef: Integer;
begin
  Result := inherited _AddRef;
  InterlockedIncrement(FRefCount);
  //ShowMessage('TPersistentTeste._AddRef: ' + IntToStr(Result)+ ' Contagem de referências: ' + IntToStr(FRefCount));
end;

function TPersistentTeste._Release: Integer;
begin
  Result := inherited _Release;
  //ShowMessage('TPersistentTeste._Release: ' + IntToStr(Result)+ ' Contagem de referências: ' + IntToStr(FRefCount));
  InterlockedDecrement(FRefCount);
  if FRefCount <=0 then
    Free;
end;

{ TComponentTeste }
procedure TComponentTeste.Testar;
begin
  ShowMessage('Testando Componente teste');
end;

function TComponentTeste._AddRef: Integer;
begin
  Result := inherited _AddRef;
  InterlockedIncrement(FRefCount);
  //ShowMessage('TComponentTeste._AddRef: ' + IntToStr(Result)+ ' Contagem de referências: ' + IntToStr(FRefCount));
end;

function TComponentTeste._Release: Integer;
begin
  Result := inherited _Release;
  //ShowMessage('TComponentTeste._Release: ' + IntToStr(Result)+ ' Contagem de referências: ' + IntToStr(FRefCount));
  InterlockedDecrement(FRefCount);
  if FRefCount <=0 then
    Free;
end;

procedure TForm1.btInterfaceObjectClick(Sender: TObject);
var
  teste: ITeste;
  Multiplasreferências: array[1..10] of ITeste;
  i: Integer;
begin
  teste := TClassTeste.create;
  teste.Testar;

  for i := 1 to 10 do
  begin
    Multiplasreferências[i] := teste;
  end;
  for i := 1 to 10 do
  begin
    Multiplasreferências[i].Testar;
  end;
end;

procedure TForm1.btInterfacePersistentClick(Sender: TObject);
var
  teste: ITeste;
  Multiplasreferências: array[1..10] of ITeste;
  i: Integer;
begin
  teste := TPersistentTeste.create;
  teste.Testar;

  for i := 1 to 10 do
  begin
    Multiplasreferências[i] := teste;
  end;
  for i := 1 to 10 do
  begin
    Multiplasreferências[i].Testar;
  end;
end;

procedure TForm1.btComponentClick(Sender: TObject);
var
  teste: ITeste;
  Multiplasreferências: array[1..10] of ITeste;
  i: Integer;
begin
  teste := TComponentTeste.create(nil);
  teste.Testar;

  for i := 1 to 10 do
  begin
    Multiplasreferências[i] := teste;
  end;
  for i := 1 to 10 do
  begin
    Multiplasreferências[i].Testar;
  end;
end;

procedure TForm1.btDelegaObjClick(Sender: TObject);
var
  Teste:  TClasseAgregada;
  Multiplasreferências: array[1..10] of ITeste;
  i: Integer;
begin
  teste := TClasseAgregada.create;
  Teste.Testar;
  for i := 1 to 10 do
  begin
    Multiplasreferências[i] := teste;
  end;
  for i := 1 to 10 do
  begin
    Multiplasreferências[i].Testar;
  end;
  Teste.Free;
end;

constructor TClasseAgregada.Create;
begin
  FITeste := TClassTeste.Create;
  inherited Create(FITeste);
end;

procedure TClasseAgregada.Testar;
begin
  FITeste.Testar;
  ShowMessage('TClasseAgregada  - teste');
end;
end.
o DFM:
object Form1: TForm1
  Left = 133
  Top = 318
  Width = 590
  Height = 321
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object btInterfaceObject: TButton
    Left = 8
    Top = 16
    Width = 121
    Height = 25
    Caption = 'TInterfacedObject'
    TabOrder = 0
    OnClick = btInterfaceObjectClick
  end
  object btInterfacePersistent: TButton
    Left = 8
    Top = 48
    Width = 121
    Height = 25
    Caption = 'TInterfacedPersistent'
    TabOrder = 1
    OnClick = btInterfacePersistentClick
  end
  object btComponent: TButton
    Left = 8
    Top = 80
    Width = 121
    Height = 25
    Caption = 'TComponent'
    TabOrder = 2
    OnClick = btComponentClick
  end
  object btDelegaObj: TButton
    Left = 8
    Top = 112
    Width = 121
    Height = 25
    Caption = 'Delegação TObject'
    TabOrder = 3
    OnClick = btDelegaObjClick
  end
  object Memo1: TMemo
    Left = 144
    Top = 13
    Width = 417
    Height = 273
    ScrollBars = ssVertical
    TabOrder = 4
  end
end
    Funciona, não altera a funcionalidade nem osresultados das nossas classes e interfaces e.... Nada de Memory Leak!
    Agora você pode me perguntar: "Por quêde tudo isso?" Simples, se você quer que um método ou umobjeto receba como parâmetro um outro objeto, porémnão quer especificar que objeto é esse, não querengessar, mas quer deixar flexível, então vocêdeverá usar interfaces, certo?
    Porem uma variável do tipo interface, emborapossa conter qualquer objeto que implemente esta interface, nãotem conhecimento de como destruí-lo, certo?
    Você não pode dizer Fteste: Iteste;Fteste := TTeste.create() e depois dar um FTeste.Destroy simplesmenteporque o método Destroy não faz parte da interface.    Você quer é não ter aresponsabilidade nem de construir o objeto, delegando essaresponsabilidade a um factory method, muito menos ter aresponsabilidade de destruí-lo. Então um objeto queimplementa uma interface deve saber destruir-se por si mesmo, senão houver nenhuma referência para ele.
    "Bom, o TInterfacedObject já sedestrói sozinho", você poderia dizer. Mas oTInterfacedPersistent e o TComponent não. E para quê eupreciso disso?
    Um dos objetivos desse artigo é criar umAbstract Factory rústico usando o RegisterClass do delphi. Essemétodo público estático registra numa listainterna do delphi referências de classes. Depois você podeencontrar essa classe com o método FindClass Nãosão referências a objetos instânciados, sãoreferências a metadata de classes. Ou seja, você podereferênciar classes por seu nome, ou por uma variavel, enão pela classe em si. Você pode criar um objeto sem saberqual é a sua classe ou mudar sua classe em runtime.
    E se você quiser instanciar uma classe, porexemplo uma form em uma variável do tipo interface,através de um abstractfactory ou através de uma classeregistrada do delphi, com FindClass e RegisterClass, vocêsimplesmente não pode chamar o método destroy ou free,porque ele não existe na interface.
    Você poderia fazer um typecast para a classedesejada, ou para object e dar um free, mas normalmente vocêdesconhece a classe a qual tem de fazer typecast, e também issopode resultar em vários acces violation na hora dedereferênciar as interfaces, visto que elas executam o _Releasede um objeto que não existe mais.
    Então vamos lá! jásaímos do assunto "1", agora estamos no "2" .

    Existe o tipo TClass, que é umareferência a uma classe (não objeto) do tipo TObject.
    Existe o tipo TInterfacedClass, que é umareferência a uma classe TInterfacedObject.
    Existe o tipo TPersistentClass que é umareferência a classe TPersistent,
    Existe TComponentClass, TFormClass, mas nãoexiste nenhum TInterfacedPersistentClass.
    E pra que eu preciso de um TInterfacedPersistent,por que não posso usar um TInterfacedObject? Ou mesmo TObjectnormal?
    Precisamos que seja interfaced porque vamostrabalhar com interfaces e queremos que nossos objetos sejam liberadosautomaticamente sem memory leak.
    E precisamos que seja descentente de TPersistentporque o método RegisterClass só registra descendentes deTPersistent. Então, o primeiro de tudo é, na suabiblioteca de classes, declare:
       
 TInterfacedPersistentClass = class of TInterfacedPersistent;
       
    Cenário: Imagine que você tem uma formde produtos e uma de consulta de produtos (TConsultaPro). Porem essasduas forms, da maneira como foram feitas, estão engessadas,são usadas no sistema inteiro, nada pode ser alterado nelas ouem sua hierarquia, e não se pode criar descententes das mesmas.
    Mas você precisa criar outras classes deconsulta de produtos, clientes, fornecedores, pedidos etc... que podemser ou não descententes de TConsultaPro e podem ser forms ouclasses que chamam forms.
    A unit1 é a unit principal do nosso programa.É o nosso cadastro de produtos.
//esta seria a unit principal do projeto

unit Unit1;

interface

uses
  Controls,
  Forms,
  Unit3,  //esta é a biblioteca onde se encontra a interface
  Classes,
  StdCtrls;

type
  TfrmPrincipal = class(TForm)
    btAbrir: TButton;
    procedure btAbrirClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
   iIntf: IFrmConsultaPro;  //A interface

end;

var
  frmPrincipal: TfrmPrincipal;

implementation

{$R *.dfm}

procedure TfrmPrincipal.btAbrirClick(Sender: TObject);
var
  NumPro: string;
  clsClasse:  TInterfacedPersistentClass;
  iIntf:  IFrmConsultaPro;
begin
  NumPro := '123456';
  clsClasse := TInterfacedPersistentClass(FindClass('TFConsultaProFactory'));
  if (clsClasse <> nil) then
  begin
    iIntf := ((clsClasse.Create) as IFrmConsultaPro);
    if iIntf <> nil then
    begin
 iIntf.ConsultaPro(NumPro);
    end;
    //ma que beleza hein!
  end;
end;
end.
o DFM:
object frmPrincipal: TfrmPrincipal
  Left = 460
  Top = 469
  Width = 288
  Height = 137
  Caption = 'Form Principal'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object btAbrir: TButton
    Left = 88
    Top = 64
    Width = 105
    Height = 25
    Caption = 'Abrir a outra Form'
    TabOrder = 0
    OnClick = btAbrirClick
  end
end
A unit2 é a unit que tem a nossa classe que não pode sermechida, a TConsultaPro:
//Unit de uma form do projeto totalmente desconhecida e que pode ser chamada
//de varios pontos do projeto ou substituida na "cara de Pau" por outra
//que implemente a mesma interface

unit Unit2;

interface

uses
  Windows,
  Controls,
  Forms,
  Dialogs,
  Unit3, //unit da interface
  SysUtils,
  Classes, StdCtrls;

type
  TFConsultaPro = class(TForm)
    edt_Produto: TEdit;
    Label1: TLabel;
    procedure edt_ProdutoKeyPress(Sender: TObject; var Key: Char);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public

  end;

implementation

{$R *.dfm}

{ TFConsultaPro } 
procedure TFConsultaPro.edt_ProdutoKeyPress(Sender: TObject;   var Key: Char);
begin
  ShowMessage('Você consultou o produto: ' + edt_Produto.Text);
end;

procedure TFConsultaPro.FormClose(Sender: TObject;   var Action: TCloseAction);
begin
  Release;     
end;

end.
o DFM:
object FConsultaPro: TFConsultaPro
  Left = 346
  Top = 305
  Width = 331
  Height = 166
  Caption = 'Consultar Produto'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnClose = FormClose
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 96
    Top = 32
    Width = 96
    Height = 13
    Caption = 'Produto Consultado:'
  end
  object edt_Produto: TEdit
    Left = 96
    Top = 48
    Width = 121
    Height = 21
    TabOrder = 0
    OnKeyPress = edt_ProdutoKeyPress
  end
end
    Vamos criar nossa interface conforme a unit 3abaixo, que é nossa unit de "biblioteca".
//unit com os tipos, classes e interfaces usadas no sistema
unit Unit3;
interface

uses
  Classes;

type
  IFrmConsultaPro = interface(IInterface)
  ['{E054C396-7551-4B79-B439-A3130B25C79E}']
    procedure ConsultaPro(NumProd: string); stdcall;
  end;
  //Um tipo de referência de classe, para podermos encontrar e instanciar um
  //objeto de uma classe e unit desconhecida pelo seu nome (string) de forma
  //que ele seja uma classe interfaceada (que implemente IInterface)
  //é uma maneira "rustica" de se fazer um factoy method
  //a propria classe a ser registrada é uma factory que so serve para instanciar
  //um objeto da classe TFConsultaPro (form que consulta produto) quando
  //se executa o método  ConsultaPro. Optei por usar uma factory que
  //implementasse a interfac, mas a propria form poderia implementa - la
  //assim eu criaria diretamente a form e não o factory.
  //optei por criar esse factory para exemplificar as vezes que você não
  //pode mecher em nada ou quase nada na form, não podendo mecher por exemplo
  //na sua linhagem.
  //faz de conta que a TFConsultaPro é uma form legada, usada no sistema
  //inteiro e que vamos fazer de tudo para não mecher nela.
  //Até mesmo mantivemos a regra de negócio no evento do edit,
  //para demosntrar como aos poucos podemos melhorar uma programação altamente
  //acoplada, totalmente estruturada ou orientada a evento e diminuir o
  //acoplamento sem ser muito traumatizante.

  TInterfacedPersistentClass = class of TInterfacedPersistent;

implementation

end.
a Unit uFactory é a unit que tem a nossa classe factory eé onde ela é registrada com RegisterClass para serencontrada com FindClass
unit uFactory;

interface

uses
    Windows,
    Classes,
    Unit2,
    Unit3;

type
  TFConsultaProFactory = class(TInterfacedPersistent,  IFrmConsultaPro)
  private
    FRefCount: Integer;
  published
    procedure ConsultaPro(NumProd: string);
stdcall;

    //métodos de IInterface
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;

implementation

procedure TFConsultaProFactory.ConsultaPro(NumProd: string);
var
  Enter: Char;
begin
  Enter := #13;
  with TFConsultaPro.Create(nil) do
  begin
    edt_Produto.Text:=NumProd;
    edt_Produto.OnKeyPress(edt_Produto,
Enter);
    ShowModal;
  end;
end;

function TFConsultaProFactory._AddRef: Integer;
begin
  Result := inherited  _AddRef;
  InterlockedIncrement(FRefCount);
end;

function TFConsultaProFactory._Release: Integer;
begin
  Result := inherited  _Release;
  InterlockedDecrement(FRefCount);
  if FRefCount <= 0 then
    Free;
end;

initialization

  //aqui eu registro minha classe factory (poderia ter registrado a form) para
  //que ela possa ser "encontrada" pelo delphi posteriormente, em uma unit que
  //não a conhece, sem esta unit 2 estar declarada no uses.

  RegisterClass(TFConsultaProFactory);

  //repare que com isso podemos instanciar objetos atraves do nome da classe
  //podendo armazenar os nomes das classes que queremos instanciar em 
  //arquivos de configuração, bancos de dados etc.

end.
    Repare que no exemplo que fizemos registramos umaclasse que possui um método para instanciar a form. Assim essaclasse teria o FactoryMethod da form, mas o registerClass e FindClassseria o FactoryMethod da nossa classe. Fizemos assim apenas parailustrar a situação de uma form feita por outra pessoaque você não pode mecher nem na unit. E num contexto quenão era orientado a objeto, mas está emmigração. Nada impede de fazer com que a própriaform implemente a interface IFrmConsultaPro, o métodoConsultaPro e que a própria form seja registrada comregisterClass. Mas o release deveria ser automatico, ou deveria-seimplementar os métodos _AddRef e _Release.
    Usando essas técnicas com criatividadevocê pode criar um super ultra abstract factory que cria qualquercomponente através de uma string, podendo permitiralterações em runtime customizadas pelo cliente emvários pontos do seu software. Também poderá teruma lista global de objetos criados e referências num objetosingleton para criar seu próprio garbage collector ou tirarestatísticas (bastando usar as interfaces e implementar essasalterações necessarias em _AddRef e _Release).
    Com isso o memory leak não te pega mais evocê pode destruir sem dó qualquer resquício deobjeto que queira ficar na memória.
    No arquivo para downloadEstudo_MemoryLeaks_Interfaces, há 4 pastas / exemplos:
    MemoryLeak_Interfaces é o código doprimeiro exemplo acima, que mostra como implementar _AddRef, _Release efazer autodestroy e refcount nas Classes TInterfacedPersistent eTComponent.
    MemoryLeak_Agregates_UsoCorreto: ilustra a maneiracorreta de se usar agregates para não gerar memory leak.
    MemoryLeak_Interface_RegisterClass mostra o exemploacima e como transformar aos poucos um sistema legado em sistemaorientado a objetos.
    MemoryLeak_Interface_RegisterClass Extra é umexemplo extra onde a classe registrada é a propria Form.

    Link para o arquivo com os exemplos, no meu skyDrive(use o 7zip para descompactar)

http://cid-a3e4fd1c20f4d546.skydrive.live.com/self.aspx/.Public/EstudoMemoryLeaksInterfaces.zip

Postagens populares

Marcadores

delphi (60) C# (31) poo (21) Lazarus (19) Site aos Pedaços (15) sql (13) Reflexões (10) .Net (9) Humor (9) javascript (9) ASp.Net (8) api (8) Básico (6) Programação (6) ms sql server (5) Web (4) banco de dados (4) HTML (3) PHP (3) Python (3) design patterns (3) jQuery (3) livros (3) metaprogramação (3) Ajax (2) Debug (2) Dicas Básicas Windows (2) Pascal (2) games (2) linguagem (2) música (2) singleton (2) tecnologia (2) Anime (1) Api do Windows (1) Assembly (1) Eventos (1) Experts (1) GNU (1) Inglês (1) JSON (1) SO (1) datas (1) developers (1) dicas (1) easter egg (1) firebird (1) interfaces (1) introspecção (1) memo (1) oracle (1) reflexão (1)