Переменные среды процесса существуют во времени выполнения и не хранятся в некотором файле или около этого. Они хранятся в собственной памяти процесса (это - то, где они, как находят, переходят к детям). Но существует виртуальный файл в
/proc/pid/environ
, Этот файл показывает все переменные среды, которые были переданы при вызове процесса (если процесс не перезаписал ту часть своей памяти — большинство программ, не делают). Ядро делает их видимыми через тот виртуальный файл. Можно перечислить их. Например, для просмотра переменных процесса 3940 можно сделать
cat /proc/3940/environ | tr '\0' '\n'
, Каждая переменная разграничена двоичным нулем от следующего. TR заменяет нуль в новую строку.
Когда у вас есть такая функция, как в вопросе, где у вас есть анонимный метод для доступа к локальной переменной, Delphi создает одного потомка TInterfacedObject, который захватывает все переменные на основе стека как свои собственные открытые переменные. Используя трюк Барри, чтобы добраться до реализации TObject и немного RTTI, мы можем увидеть все это в действии.
Волшебный код реализации, вероятно, выглядит следующим образом:
// Magic object that holds what would normally be Stack variables and implements
// anonymous methods.
type ProcedureThatUsesAnonymousMethods$ActRec = class(TInterfacedObject)
public
V: string;
function AnonMethodImp: string;
end;
// The procedure with all the magic brought to light
procedure ProcedureThatUsesAnonymousMethods;
var MagicInterface: IUnknown;
F1: TFunc<string>;
F2: TFunc<string>;
begin
MagicInterface := ProcedureThatUsesAnonymousMethods$ActRec.Create;
try
F1 := MagicInterface.AnonMethod;
MagicInterface.V := '1';
F2 := MagicInterface.SomeOtherAnonMethod;
MagicInterface.V := '2';
ShowMessage(F1);
ShowMessage(F2);
finally MagicInterface := nil;
end;
end;
Конечно, этот код не компилируется. Я без магии :-) Но идея здесь в том, что за кулисами создается «магический» объект, а локальные переменные, на которые ссылается анонимный метод, преобразуются в открытые поля магического объекта. Этот объект используется в качестве интерфейса (IUnkown), поэтому он получает счетчик ссылок. По-видимому, один и тот же объект захватывает все используемые переменные И определяет все анонимные методы.
Это должно отвечать и «когда», и «как».
Вот код, который я использовал для расследования. Поместите TButton в чистую форму, это должна быть целая единица. Когда вы нажмете кнопку, вы увидите следующее на экране в последовательности:
TForm25.Button1Click$ActRec: TInterfacedObject
: Здесь показан объект, стоящий за реализацией, он получен из TInterfacedObject OnStack:string
: RTTI обнаруживает это поле в этом объекте. Self: TForm25
: RTTI обнаруживает это поле на этом объекте. Он используется для получения значения ClasVar
FRefCount:Integer
- это происходит из TInterfacedObject Class Var
- результат ShowMessage. On Stack
- результат ShowMessage. Вот код:
unit Unit25;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Rtti;
type
TForm25 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
ClassVar: string;
public
end;
var
Form25: TForm25;
implementation
{$R *.dfm}
procedure TForm25.Button1Click(Sender: TObject);
var F1: TFunc<string>;
F2: TFunc<string>;
OnStack: string;
i: IInterface;
o: TObject;
RC: TRttiContext;
R: TRttiType;
RF: TRttiField;
begin
// This anonymous method references a member field of the TForm class
F1 := function :string
begin
Result := ClassVar;
end;
i := PUnknown(@F1)^;
o := i as TObject;
ShowMessage(IntToStr(Integer(o))); // I'm looking at the pointer to see if it's the same instance as the one for the other Anonymous method
// This anonymous method references a stack variable
F2 := function :string
begin
Result := OnStack;
end;
i := PUnknown(@F2)^;
o := i as TObject;
ShowMessage(IntToStr(Integer(o)));
ShowMessage(o.ClassName + ': ' + o.ClassType.ClassParent.ClassName);
RC.Create;
try
R := RC.GetType(o.ClassType);
for RF in R.GetFields do
ShowMessage(RF.Name + ':' + RF.FieldType.Name);
finally RC.Free;
end;
ClassVar := 'Class Var';
OnStack := 'On Stack';
ShowMessage(F1);
ShowMessage(F2);
end;
end.