Q: How do I determine if a variable, which should have been assigned with a TMyOtherObj.create(), but wasn't?

Q: How do I determine if a variable, which should have been assigned with a TMyOtherObj.create(), but wasn't?

It appears that Assigned() does not do this; as evaluating a

Here's a quick sample code (not intended to be compile-able), just an attempt to document the question. 

procedure TMyObj.AFunc(var AReturn : TMyOtherObj)
begin
   if (Assigned(AReturn)) then
      AReturn.DoSomeThing();
end;

var
   AObj : TMyObj;
   AReturn : TMyOtherObj;
begin
   AObj := TMyObj.create();
   try
      //FORGOT THIS LINE
      //AReturn := TMyOtherObj.create();

      AObj.AFunc(AReturn);
   finally
      AObj.Free();
   
end.

Comments

  1. As you already have figured out, Delphi does not initialize local variables. There are some exceptions to this rule though:
    Strings, dynamic arrays and interfaces.
    Also, global variables and fields (of objects) are initialized to zero (nil, false or whatever zero means for the variable type).
    A for FreeAndNil: You might be surprised that some people argue that using it is considered bad practice. (I am not one of them.).

    ReplyDelete
  2. Yeah, it was surprising that I can make an TObject with properties for Events and they are always NIL'd out and a simple Assigned(TObject.AEvent) allows me to call it. Was bummed out that this is only for certain cases.

    TMyOtherObj = class(TObject)
    end;

    MyEvent = procedure(AObj : TMyObj) of object;

    TMyObj = class(TObject)
    private
       FEvent : TMyEvent
    public
       property Event : TNotifyEvent
          read FEvent
          write FEvent
       procedure TriggerEvent(AObj : TMyOtherObj);
    end;

    ..

    procedure TMyObj.TriggerEvent();
    var
       AMyOtherObj : TMyOtherObj;
    begin
       AMyOtherObj := NIL; // * new template to NIL first

       if (assigned(self.FEvent)) then
       begin
          // oops, forgot ... AMyOtherObj := TMyOtherObj.create();
          try
             self.FEvent(AMyOtherObj);
          finally
             FreeAndNil(AMyOtherObj);
          end;
       end;
    end;

    ...

    In the end, setting the variable to NIL each time is something to remember. Just like remembering to assigned it with a TObject.create(). So it makes it easier to spot, but not a terrific method. 

    In regards to the FreeAndNil(), I'm not sure where I land on this... personally, anything that helps me recover from a mistake is nice. I understand that there's no need to perform a FreeAndNil(), if you don't forget to correctly deal with a variable after it was *.Free()'d. But then again, I don't make any errors in my code that I intend. My goal this past couple weeks is to find all memory related issues. Everything I've found so far was a mistake, something I forgot to do first. Admittedly, several of them have been the result of using a "var" parameter to return an object and not strictly adhering to a "factory"; mainly because I thought Assigned() would help me determine if the object was created before calling. Big mistake.

    ReplyDelete
  3. TObject zeroes its memory when beign crated. It is not about events only, it is abotu all the inner variables.

    That is one of the reasons creating classes instances is slow and expensive, comparing to stack-based local variables as records or old-style objects

    ReplyDelete

Post a Comment