10.5 Замечание по видимости и времени жизни хелперов записей и типов

На верх  Назад  Вперёд

Для классов, время жизни экземпляра класса явно управляется программистом. Поэтому ясно, что означает параметр Self и когда он действителен.

Записи и другие простые типы находятся в стеке, что означает, что они выходят из области видимости, когда функция, процедура или метод, в котором они используются, заканчивается.

В сочетании с тем, что методы хелпера имеют тип, совместимый с методами класса, и они могут быть использованы в качестве обработчиков событий, но это может привести к неожиданным ситуациям: указатель данных (Data) в  методе хелпера устанавливается на адрес переменной.

Рассмотрим следующий пример:

{$mode objfpc}

{$modeswitch typehelpers}

uses

  Classes;

 

type

  TInt32Helper = type helper for Int32

    procedure Foo(Sender: TObject);

  end;

 

procedure TInt32Helper.Foo(Sender: TObject);

begin

  Writeln(Self);

end;

 

var

  i: Int32 = 10;

  m: TNotifyEvent;

begin

  m := @i.Foo;

  WriteLn('Данные: ',PtrUInt(TMethod(m).Data));

  m(nil);

end.

Этот код выдаст что-то вроде (фактическое значение поля Data может отличаться):

Данные: 6848896

10

Переменная i все еще находится в области видимости, когда m вызывается.

Но изменение кода на

{$mode objfpc}

{$modeswitch typehelpers}

uses

  Classes;

 

type

  TInt32Helper = type helper for Int32

    procedure Foo(Sender: TObject);

  end;

 

procedure TInt32Helper.Foo(Sender: TObject);

begin

  Writeln(Self);

end;

 

Function GetHandler :TNotifyEvent;

var

  i: Int32 = 10;

begin

  Result:=@i.foo;

end;

 

Var

  m: TNotifyEvent;

begin

  m := GetHandler;

  WriteLn(PtrUInt(TMethod(m).Data));

  m(nil);

end.

Выдаст:

140727246638796

0

Реальное значение будет зависеть от архитектуры, но дело в том, что i больше не находится в области видимости, делая вывод своего значения неопределенным, и возможно даже приводя к нарушениям доступа и сбоям в программе.