Pesquisar

quinta-feira, 19 de março de 2015

AccessViolation em Interface

Olá pessoal.

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.

sexta-feira, 6 de março de 2015

Os Segredos de TStringList - Parte 1

Saudações a todos.

Creio que uma das classes mais versateis do Delphi é a TStringList. Como uma postagem sobre esta classe seria muito extensa, dividi em partes o assunto.

Nesta séries de postagens pretendo explorar alguns recursos que não são muito conhecidos sobre essa classe.

AddObject

Esta propriedade é particularmente útil quando queremos associar um objeto a uma string.

Ex:

var
  Lista: TStringList;
begin
  Lista:= TStringList.Create;
  try
    Lista.AddObject('FormulárioPrincipal', frmPrincipal);
  finally
    Lista.Free;
  end;
end;
Obviamente, como este é o único item na lista, então o index dessa operação será 0.

Para referenciarmos a string nesta posição, podemos usar a seguinte linha:

ShowMessage(Lista.Strings[0]);


E para referenciasmo o obejto nesta mesma posição fazemos assim:

(Lista.Objects[0] as TForm).Show;


Note que como a Lista não guarda o tipo de objeto, portanto é necessário realizarmos o typecast para TForm, claro, isso porque sabemos que o objeto na lista é um TForm (Este problema pode muito bem ser resolvido por Generis, mas isso fica para outro post).

Muito cuidado ao utilizar typecast de objetos, pois como sabemos é muito facil gerar um  "Acess Violention" nesta operação!!

Outro cuidado que devemos tomar ao utilizar um StringList para armazenar objetos é lembrar que, caso estejamos trabalhando com uma lista ordenada (TStringList.Sorted:= True) e não permitirmos duplicidade (TStringList.Duplicates:= dupError;) adicionar o mesmo objetos mais de uma vez na lista ira gerar uma exceção do tipo 'EListError'.

Mais uma consideração que devemos ter quando utilizamos listas de objetos com o TStringList é a propriedade 'TStringList.OwnsObjects'.

Esta propriedade, quando setada para True indica ao Delphi que o dono dos obejtos na lista é o TStringList, e quando liberarmos o TStringList com Free, o delphi também executara o Free de todos os obejtos na lista.

Isso pode ser ótimo ou não dependendo do contexto da sua rotina.

Abraços a todos e até a próxima postagem.

quinta-feira, 5 de março de 2015

Padrões de Desenvolvimento (Design Patterns) - Singleton

E ae pessoal,  tudo certo?

Nesses primeiros posts eu vou escrever um pouco sobre algumas padrões de desenvolvimento (Design Patterns ) que considero importante, a primeira será Singleton.

Na engenharia de software, o padrão de desenvolvimento (Design Pattern) Singleton é um padrão de projeto que restringe a instanciação de uma classe para um objeto. Isso se torna bem útil quando um objeto é necessário para coordenar as ações em todo o sistema.

Quando necessitamos de somente uma instância da classe?

Por exemplo, uma classe de conexão com banco de dados, vamos supor que você terá que chamar diversas vezes a conexão com o banco de dados em um código na mesma execução, se você instanciar toda vez a classe de banco, haverá grande perda de desempenho, assim usando o padrão Singleton, é garantida que nesta execução será instanciada a classe somente uma vez.


Então como saber se uma classe é um Singleton?

Simplesmente obtenha várias vezes a instancia e compare com as anteriores. A classe não é singleton se existir pelo menos uma que não seja igual, a forma de comparar dois objetos a e b tal que a = b é falso.

O padrão singleton necessita que o objeto seja criado pela propria classe, no Delphi não conseguimos suprimir o construtor de TObject (Create), mas existe uma técnica para esconde-lo no auto completar (auto-complete);

Exemplo simples de implementação:
interface

type
  TConexaoComBancoDeDados = class
  strict private
    class var fInstance: TConexaoComBancoDeDados;
    class function GetInstance: TConexaoComBancoDeDados; static;
    constructor Create; virtual; //simplesmente para não aparecer no auto-completar
  public
    class property Instance : TConexaoComBancoDeDados read GetInstance;
  end;

implementation

{ TConexaoComBancoDeDados }

constructor TConexaoComBancoDeDados.Create;
begin
end;

class function TConexaoComBancoDeDados.GetInstance: TConexaoComBancoDeDados;
begin
  if Assigned(fInstance) then
  begin
    fInstance := TConexaoComBancoDeDados.Create;
  end;

  Result := fInstance;
end;


Aqui um teste, pra comprovar o funcionamento:
procedure TForm1.btn1Click(Sender: TObject);
var
  lConexoes01: TConexaoComBancoDeDados;
  lConexoes02: TConexaoComBancoDeDados;
begin
  //..Criando as conexões
  lConexoes01 := TConexaoComBancoDeDados.Instance;

  try

    lConexoes02 := TConexaoComBancoDeDados.Instance;
    try
      // uma compração direta de objetos
      if lConexoes01 = lConexoes02 then
      begin
        ShowMessage('Classe implementa Singleton');
      end else begin
        ShowMessage('Classe NÃO implementa Singleton');
      end;

    finally
      lConexoes02.Free;
    end;
  finally
    lConexoes01.Free;
  end;

end;


Para conseguir perceber a diferença, modifique a método GetInstance da classe TConexaoComBancoDeDados:
class function TConexaoComBancoDeDados.GetInstance: TConexaoComBancoDeDados;
begin
{
  if Assigned(fInstance) then
  begin
    fInstance := TConexaoComBancoDeDados.Create;
  end;

  Result := fInstance;
}
  Result := TConexaoComBancoDeDados.Create;
end;

Há críticas ao uso do padrão Singleton, como alguns consideram que é uma má prática (anti-pattern), julgando que ele usado em demasia, introduz restrições desnecessárias em situações em que uma única instância de uma classe não é realmente necessário, e introduz estado global em um aplicativo. Eu particularmente não concordo.

Resumidamente, mostrei como é feito um Singleton em Delphi, em futuras postagens mostraremos ele sendo usado em "produção"..

Abrass

quarta-feira, 4 de março de 2015

Novo formato dos Tauras

Boa tarde leitores do blog.

Como eu já não tinha muito tempo livre, convidei mais 2 colegas de programação para me ajudarem nas postagens.

A ideia é ter pelo menos um post por semana. Sendo um post de cada vez de cada um dos colaboradores do blog.

O nível de conhecimento do blog também subiu um pouco, agora trataremos de assuntos mais complexos, mas nada que alguém com interesse em aprender não entenda.
Embora nenhum de nós sejamos MVP da embarcadero (Most Valuable Professional / Profissional de Maior Valor) temos acesso muito facilmente a vários deles no Brasil e fora do pais, que nos ajudarão tirando nossas dúvidas e gerando conteúdo.

Como puderam ver nas apresentações, todos nós temos formação completa oficial da embarcadero e somos certificados. Comento isso para dar mais credibilidade ao conteúdo que publicaremos.

Lembro também que não somos donos da verdades, apenas temos bastante experiência em programação Delphi, portanto, nenhum de nos esta livre de postarmos uma informação errada, mas podem ter certeza que correremos atrás das informações mais corretas possíveis.

Espero também que os leitores nos ajudem e participem bastante das postagens através dos comentários, isso nos ajudará a saber os conteúdos que vocês leitores mais gostam.

Espero que o conteúdo ajude a todos!

Obrigado pela atenção e aproveitem o próximo post, que imagino ser do Alan...

Apresentação III

Buenas Delpheiros...

Meu nome é Rafael Stange, trabalho com Delphi a mais ou menos 10 anos, vide colegas do blog. Atualmente trabalho na Aquasoft Tecnologia da Informação(Centro de treinamentos oficial Embarcadero) prestando consultoria especializada e outsourcing.

Possuo certificação Delphi Master Developer além da formação e certificação Delphi Developer. Em seguida estaremos postando assuntos dos mais diversos temas sempre utilizando nosso bom e velho Delphi. Teremos postagens Retro e muitas novidades das novas versões do Delphi.

Abraço a todos, Enjoy the blog!

Apresentação II

Bem-vindos

Me chamo Alan Vieceli.
Sou programador a pouco mais de 10 anos, igual meus colegas Maico e Rafael.

Já tive experiências profissionais em outras linguagens, como PHP, Java e C++, das 3 linguagens, PHP é a que mais gosto e tenho um certo conhecimento mais profundamente. Além do Delphi é claro.

Atualmente possuo a Delphi Developer Certification, reconhecida pela Embarcadero. Estou trabalhando arduamente pra conseguir a Delphi Master Certification, assim que que consegui-la eu postarei.

Trabalho com outsourcing e consultoria pela empresa Aquasoft.

Nossa intenção é realizar postagens semanais sobre a ferramenta, explicações e códigos úteis, além de algumas coisas mais avançadas que vão ser úteis no dia a dia.

Abrasss.