Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the scope of Self?

Tags:

delphi

I am asking to advance my understanding of Self.

Please consider the following:

type
  PTestObject = ^TTestObject;
  TTestObject = class(TObject)
  private
    FCaption : String;
  public
    procedure MakeThePointer;

    property Caption : String read FCaption write FCaption;
  end;

  TForm4 = class(TForm)
    ButtonFirst: TButton;
    ButtonSecond: TButton;
    ButtonThird: TButton;
    procedure ButtonFirstClick(Sender: TObject);
    procedure ButtonSecondClick(Sender: TObject);
    procedure ButtonThirdClick(Sender: TObject);
  private
  public
  end;

var
  Form4: TForm4;
  PointerOfTest : PTestObject;
  TestObj : TTestObject;

implementation

{$R *.dfm}

procedure TTestObject.MakeThePointer;
begin
  PointerOfTest := @Self;
end;

procedure TForm4.ButtonFirstClick(Sender: TObject);
begin
  TestObj := TTestObject.Create;
  TestObj.Caption := 'Hello';
  TestObj.MakeThePointer;
end;

procedure TForm4.ButtonSecondClick(Sender: TObject);
begin
  TestObj.MakeThePointer;
  ShowMessage(PointerOfTest^.Caption);
end;

procedure TForm4.ButtonThirdClick(Sender: TObject);
begin
  // TestObj.MakeThePointer; - Because I do not do this I get Access Violation
  ShowMessage(PointerOfTest^.Caption);
end;

The idea is to create a pointer to TestObj's Self and then later access it again. If I call MakeThePointer in the same Click event (ButtonSecondClick) where I access that pointer it works fine. If I do not call MakeThePointer before accessing the pointer (ButtonThirdClick) then it seems that TestObj's Self does not exist in such a way that the previously created pointer is valid and I get an Access Violation.

Please correct me if I am wrong but I assume that Self is a variable local to each of the object's methods. Thus it will have scope only for each of that methods separately?

Now consider this... If that is the case then why does the following work if ButtonFirst is clicked, then ButtonSecond? It seems that the Self variable has landed on the same address thus allowing the following to work. May I assume that the Self variable will always be on the same address or will it change?

type
  TFormOther = class(TForm)
    ButtonFirst: TButton;
    ButtonSecond: TButton;
    procedure ButtonFirstClick(Sender: TObject);
    procedure ButtonSecondClick(Sender: TObject);
  private
  public
    procedure MakeThePointer;
    procedure SetTheCaption;
  end;

var
  FormOther: TFormOther;
  PointerOfForm : ^TForm;

implementation

{$R *.dfm}

procedure TFormOther.MakeThePointer;
begin
  PointerOfForm := @Self;
end;

procedure TFormOther.SetTheCaption;
begin
  PointerOfForm^.Caption := 'Hello';
end;

procedure TFormOther.ButtonFirstClick(Sender: TObject);
begin
  MakeThePointer;
end;

procedure TFormOther.ButtonSecondClick(Sender: TObject);
begin
  SetTheCaption;
end;
like image 929
Blurry Sterk Avatar asked Feb 07 '17 08:02

Blurry Sterk


1 Answers

What is the scope of Self?

In a method Self is best thought of as a local variable. Its address, @Self, is therefore valid until the method returns.

This explains why your code fails. Your code dereferences the pointer after the method has returned, by which point the pointer is not valid.

May I assume that the Self variable will always be on the same address?

No you may not.


I think your problems start here:

type
  PTestObject = ^TTestObject;

Because TTestObject is a class, a variable of type TTestObject, such as your Self, is a reference. A reference is a fancy name for a pointer. In this case, your Self, inside methods of TTestObject is a pointer to the instance.

So use TTestObject instead of ^TTestObject and your problems dissolve.

like image 81
David Heffernan Avatar answered Oct 16 '22 01:10

David Heffernan