unit Unit2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TProc = procedure(AParametro: String) of object;
  PProc = ^TProc;
  TForm2 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    procedure Proc1;
    procedure Proc2(AParametro: String);
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
var
  A: TProc;
  B: PProc;
begin
  // A contém uma referência a Proc2. Como A é um ponteiro de método e não
  // ponteiro comum, a referência pode ser direta, pois TProc define uma
  // assinatura que é compatível com "Proc2".
  A := Proc2;

  // B não é um ponteiro de método. B é um ponteiro para um ponteiro de método,
  // pois ele aponta para TProc (que é o ponteiro de método). Por ser um
  // ponteiro para algo, ele precisa ter sua memória alocada antes de ser usado,
  // do contrário haverá lixo dentro dele. Para alocar se usa GetMem. Como B
  // aponta para TProc, o segundo parâmetro de GetMem será o "tamanho do
  // apontado", por isso usa-se SizeOf(TProc)
  GetMem(B,SizeOf(TProc));
  try
    // Neste ponto, B é um espaço de memória alocada que espera receber um
    // TProc, mas ainda está apontando para nil. Se você fizer B := @A, você
    // estará descartando a memória alocada na instrução anterior e estará
    // dizendo que B e A estão no mesmo lugar da memória, já que @A significa
    // "endereço de A" ou "local da memória onde A está". Como B é um ponteiro
    // para um ponteiro de método, se você fizer B := @A, B vai assumir que
    // aquele endereço contém um "poneiro para um ponteiro de método", quando na
    // verdade ele possui apenas um "ponteiro de método", ou seja, você estará,
    // grosso modo, formatando um espaço de memória de forma errada e é por isso
    // que o Access Violation ocorria. Ao fazer B^ := A, por outro lado, você
    // está dizendo que "o valor apontado por B é A". Ora, se B aponta para um
    // TProc e A é um TProc, ao fazer B^ := A, você estará preenchendo as
    // lacunas que faltavam para que B seja uma variável completa e carregada
    // com o valor correto! Novamente, grosso modo, fazendo desta forma você
    // estará formatando a memória corretamente
    B^ := A;
    // Ao executar o ShowMessage, você vai notar que os endereços de B e de A
    // são diferentes e isso é natural, porque B foi alocado com GetMem e também
    // porque "B aponta para um ponteiro de método" enquanto "A é um ponteiro de
    // método", logo, são variáveis que apontam para coisas distintas e é
    // natural que seus endereços sejam diferentes. Por outro lado, o endereço
    // daquilo que é apontado por B é igual ao endereço de A, como se pode
    // imaginar
    Showmessage('Endereço de A: 0x' + IntToHex(Integer(@a),2) + #13#10'Endereço de B: 0x' + IntToHex(Integer(@b),2) + #13#10'Endereço de B^ (aquilo que B aponta): 0x' + IntToHex(Integer(@b^),2));
    // Como A é um ponteiro de método, ele não precisa de maiores artifícios. A
    // é um alias para um método (no caso Proc2) e por isso pode ser chamado
    // diretamente. Note que é A indistinguível de Proc2, pois pode ser
    // executado inclusive com parâmetros sem problema algum
    A('Via variável A');
    // B, por outro lado é um ponteiro para um ponteiro de método, logo,
    // executar diretamente B não é possível, porque B sozinho é só um ponteiro
    // para um ponteiro de método. Aquilo que B aponta é que pode ser executado
    // e é por isso que para que B possa ser executado como Proc2 precisamos
    // acessar aquilo que ele está apontando (TProc, o ponteiro de método) e
    // para isso se usa a deferência
    B^('Via variável B');
  finally
    // Dispensa comentários;
    FreeMem(B);
  end;
end;

procedure TForm2.Proc1;
begin
  ShowMessage('Chamou Proc1');
end;

procedure TForm2.Proc2(AParametro: String);
begin
  ShowMessage('Chamou Proc2: ' + AParametro);
end;

end.