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 больше не находится в области видимости, делая вывод своего значения неопределенным, и возможно даже приводя к нарушениям доступа и сбоям в программе. |