Dynamic Component Creation Gotcha (Don't Do This)
In the Dynamic Component Creation article we discussed the following code sample that demonstrated the wrong way to dynamically create and free a TComponent descendant that is only needed within a single method (created and freed in the same block of code).
In the article a warning was issued against dynamically creating components with valid owners and then explicitly freeing the new objects afterwards.
The following is an example of this (the TTable class is used for this example, but the issues raised in this article concern the dynamic creation of any TComponent descendant).
Looking at the code, the "Free" statement will always be called as long as the TTable creation is successful, which means the Create class method returns without raising an exception. Because of this, the TTable component, no matter what, will be alive (instantiated, assigned, and valid) only within the try/finally code block above.
Unfortunately, this "redundant safety net" can introduce the following undesirable side-effects:
Conflicting and Confusing Code
One problem with this code is that code readability suffers. The initial creation call where we pass "Self" as the owner conflicts with the Free method call in the Finally block.
Passing Self as the owner means that Self will destroy the TTable instance we just created. The last line of code (the call to "Free;") means the dynamically created TTable will not be destroyed by Self, but instead it will be destroyed programmatically.
This conflict in readability can increase the cost of maintaining the code.
Expensive Code Maintenance
When a maintenance programmer encounters this code, how is the problem fixed? Should the Free method be removed, or should nil be the owner? Is there another component somewhere that has overridden the Notification method intentionally and is waiting for this local TTable to be created? Is this really a problem? Should it be fixed, or left alone? What was the original programmer's intention?
The fact that maintenance programmers need to even ask these questions indicates a serious problem.
More problematic is that the answers to these questions can reside outside of the code sample presented here, requiring an intimate source-level understanding of every component in use on the form, as well as a good understanding of the context surrounding the code.
Performance Hit on Construction - 1
When our TTable is created, the Create constructor is called for TTable, and for each ancestor class that overrides the Create constructor, all the way to TComponent.Create. Here's the implementation for the TComponent.Create method:
constructor TComponent.Create(AOwner: TComponent) ;
begin
  FComponentStyle := [csInheritable];
  if AOwner <> nil then AOwner.InsertComponent(Self) ;
end;
Notice that if AOwner is nil, the Create constructor is extremely fast.
However, if AOwner is not nil, then AOwner's InsertComponent method is called. InsertComponent looks like this:
~~~~~~~~~~~~~~~~~~~~~~~~~
procedure TComponent.InsertComponent(AComponent: TComponent) ;
begin
  AComponent.ValidateContainer(Self) ;
  ValidateRename(AComponent, '', AComponent.FName) ;
  Insert(AComponent) ;
  AComponent.SetReference(True) ;
  if csDesigning in ComponentState then
    AComponent.SetDesigning(True) ;
  Notification(AComponent, opInsert) ;
end;
~~~~~~~~~~~~~~~~~~~~~~~~~
Note: A test program was created in Delphi to time the dynamic creation of 1000 components with varying initial component counts.
A number of methods are called here, but without a doubt the most expensive (in terms of having a negative impact on performance) is the call to Notification. Notification looks like this:
~~~~~~~~~~~~~~~~~~~~~~~~~
procedure TComponent.Notification(AComponent: TComponent; Operation: TOperation) ;
var
  j: Integer;
begin
  if (FFreeNotifies <> nil) and (Operation = opRemove) then
  begin
    FFreeNotifies.Remove(AComponent) ;
    if FFreeNotifies.Count = 0 then
    begin
      FFreeNotifies.Free;
      FFreeNotifies := nil;
    end;
  end;
  if FComponents <> nil then
    for j := 0 to FComponents.Count - 1 do
      TComponent(FComponents[j]).Notification(AComponent, Operation) ;
end;
~~~~~~~~~~~~~~~~~~~~~~~~~
This time-intensive code here is the last for-loop, which iterates through every single owned component, calling Notification again (it's a iterative call, so every component either owned or indirectly owned by the form (or whatever component was initially passed as the AOwner parameter), will have this method called. Additionally, the Notification method is virtual, so it can be overridden in descendant classes (and often is).
When the TTable instance is destroyed (the call to "Free" in the Finally block), ultimately the TComponent destructor is called (Free checks the instance to see if it's non-nil and then calls Destroy). TComponent.Destroy looks like this:
destructor TComponent.Destroy;
var
  j: Integer;
begin
  if FFreeNotifies <> nil then
  begin
    for j := 0 to FFreeNotifies.Count - 1 do
      TComponent(FFreeNotifies[j]).Notification(Self, opRemove) ;
    FFreeNotifies.Free;
    FFreeNotifies := nil;
  end;
  Destroying;
  DestroyComponents;
  if FOwner <> nil then FOwner.RemoveComponent(Self) ;
  inherited Destroy;
end;
Notice near the end of this method the check to see if "FOwner <> nil", followed by the call to the owner's RemoveComponent method. In other words, when this TTable instance was created, an owner was specified in the constructor, then we'll call that owner's RemoveComponent method later when the TTable instance is destroyed. RemoveComponent looks like this...
procedure TComponent.RemoveComponent(AComponent: TComponent) ;
begin
  Notification(AComponent, opRemove) ;
  AComponent.SetReference(False) ;
  Remove(AComponent) ;
  AComponent.SetDesigning(False) ;
  ValidateRename(AComponent, AComponent.FName, '') ;
end;
Notice at the beginning of this method the call to Notification (passing opRemove). Notification, as we've already seen above, is a iterative call and it's a virtual method.
So the additionally unnecessary performance hit gets you twice. Once on creation and once on destruction. This performance hit can be completely avoided by passing nil as the parameter to our TTable instance that is locally created and freed.
When you specify an owner in the constructor of a TComponent, the owner's internal component list gains a new reference to your component. If the owner does not own any components, the list does not yet exist and is subsequently created. The amount of memory consumed here could easily be called negligible, but it is unnecessary all the same.
Notification is used for a number of purposes. One of these uses is auto-hookup. Auto-hookup occurs when a component that needs to link to another component overrides the Notification method, looking for a component of that type. When it's found (assuming the linked property is not already set), it connects automatically. A number of third-party component packages have these. Here's a variation of auto-hookup from the VCL (taken from DBTables.pas):
procedure TSession.Notification(AComponent: TComponent; Operation: TOperation) ;
begin
  inherited Notification(AComponent, Operation) ;
  if AutoSessionName and (Operation = opInsert) then
    if AComponent is TDBDataSet then
      TDBDataSet(AComponent).FSessionName := Self.SessionName
    else if AComponent is TDatabase then
      TDatabase(AComponent).FSession := Self;
end;
When "Self" is the owner, Delphi will pass your dynamically created component to every other component on the form, unnecessarily exposing your component to unexpected third-party auto-hookup code. This code can change your component's properties and assign handlers to it's events.
This code also introduces the risk of Delphi attempting to free the dynamically-created instance twice (this is a bad thing, and can result in AVs and other problems). This will happen if the owner is freed within the Try/Finally block.
This could happen unexpectedly if the code inside the Try block was time-intensive, and a method within the block directly or indirectly called Application.ProcessMessages.
If this condition existed, and the user closed the form while execution was in the Try block, then the form would be destroyed in the call to Application.ProcessMessages. When the form is destroyed, it also destroys all owned components (including the TTable). At this point in the execution, the reference created by "TTable.Create" is invalid. Any further references to that TTable object (e.g., calling methods or setting properties of TTable) would most likely result in access violations, as would the final call to Free. If this were to occur, it would be a problem that would be extremely difficult to find, debug, and ultimately trace back to the fact that you should have passed nil in as the owner to the dynamically created and locally-used TTable.
In the Dynamic Component Creation article we discussed the following code sample that demonstrated the wrong way to dynamically create and free a TComponent descendant that is only needed within a single method (created and freed in the same block of code).
In the article a warning was issued against dynamically creating components with valid owners and then explicitly freeing the new objects afterwards.
The following is an example of this (the TTable class is used for this example, but the issues raised in this article concern the dynamic creation of any TComponent descendant).
Looking at the code, the "Free" statement will always be called as long as the TTable creation is successful, which means the Create class method returns without raising an exception. Because of this, the TTable component, no matter what, will be alive (instantiated, assigned, and valid) only within the try/finally code block above.
Unfortunately, this "redundant safety net" can introduce the following undesirable side-effects:
Conflicting and Confusing Code
One problem with this code is that code readability suffers. The initial creation call where we pass "Self" as the owner conflicts with the Free method call in the Finally block.
Passing Self as the owner means that Self will destroy the TTable instance we just created. The last line of code (the call to "Free;") means the dynamically created TTable will not be destroyed by Self, but instead it will be destroyed programmatically.
This conflict in readability can increase the cost of maintaining the code.
Expensive Code Maintenance
When a maintenance programmer encounters this code, how is the problem fixed? Should the Free method be removed, or should nil be the owner? Is there another component somewhere that has overridden the Notification method intentionally and is waiting for this local TTable to be created? Is this really a problem? Should it be fixed, or left alone? What was the original programmer's intention?
The fact that maintenance programmers need to even ask these questions indicates a serious problem.
More problematic is that the answers to these questions can reside outside of the code sample presented here, requiring an intimate source-level understanding of every component in use on the form, as well as a good understanding of the context surrounding the code.
Performance Hit on Construction - 1
When our TTable is created, the Create constructor is called for TTable, and for each ancestor class that overrides the Create constructor, all the way to TComponent.Create. Here's the implementation for the TComponent.Create method:
constructor TComponent.Create(AOwner: TComponent) ;
begin
  FComponentStyle := [csInheritable];
  if AOwner <> nil then AOwner.InsertComponent(Self) ;
end;
Notice that if AOwner is nil, the Create constructor is extremely fast.
However, if AOwner is not nil, then AOwner's InsertComponent method is called. InsertComponent looks like this:
~~~~~~~~~~~~~~~~~~~~~~~~~
procedure TComponent.InsertComponent(AComponent: TComponent) ;
begin
  AComponent.ValidateContainer(Self) ;
  ValidateRename(AComponent, '', AComponent.FName) ;
  Insert(AComponent) ;
  AComponent.SetReference(True) ;
  if csDesigning in ComponentState then
    AComponent.SetDesigning(True) ;
  Notification(AComponent, opInsert) ;
end;
~~~~~~~~~~~~~~~~~~~~~~~~~
Note: A test program was created in Delphi to time the dynamic creation of 1000 components with varying initial component counts.
A number of methods are called here, but without a doubt the most expensive (in terms of having a negative impact on performance) is the call to Notification. Notification looks like this:
~~~~~~~~~~~~~~~~~~~~~~~~~
procedure TComponent.Notification(AComponent: TComponent; Operation: TOperation) ;
var
  j: Integer;
begin
  if (FFreeNotifies <> nil) and (Operation = opRemove) then
  begin
    FFreeNotifies.Remove(AComponent) ;
    if FFreeNotifies.Count = 0 then
    begin
      FFreeNotifies.Free;
      FFreeNotifies := nil;
    end;
  end;
  if FComponents <> nil then
    for j := 0 to FComponents.Count - 1 do
      TComponent(FComponents[j]).Notification(AComponent, Operation) ;
end;
~~~~~~~~~~~~~~~~~~~~~~~~~
This time-intensive code here is the last for-loop, which iterates through every single owned component, calling Notification again (it's a iterative call, so every component either owned or indirectly owned by the form (or whatever component was initially passed as the AOwner parameter), will have this method called. Additionally, the Notification method is virtual, so it can be overridden in descendant classes (and often is).
When the TTable instance is destroyed (the call to "Free" in the Finally block), ultimately the TComponent destructor is called (Free checks the instance to see if it's non-nil and then calls Destroy). TComponent.Destroy looks like this:
destructor TComponent.Destroy;
var
  j: Integer;
begin
  if FFreeNotifies <> nil then
  begin
    for j := 0 to FFreeNotifies.Count - 1 do
      TComponent(FFreeNotifies[j]).Notification(Self, opRemove) ;
    FFreeNotifies.Free;
    FFreeNotifies := nil;
  end;
  Destroying;
  DestroyComponents;
  if FOwner <> nil then FOwner.RemoveComponent(Self) ;
  inherited Destroy;
end;
Notice near the end of this method the check to see if "FOwner <> nil", followed by the call to the owner's RemoveComponent method. In other words, when this TTable instance was created, an owner was specified in the constructor, then we'll call that owner's RemoveComponent method later when the TTable instance is destroyed. RemoveComponent looks like this...
procedure TComponent.RemoveComponent(AComponent: TComponent) ;
begin
  Notification(AComponent, opRemove) ;
  AComponent.SetReference(False) ;
  Remove(AComponent) ;
  AComponent.SetDesigning(False) ;
  ValidateRename(AComponent, AComponent.FName, '') ;
end;
Notice at the beginning of this method the call to Notification (passing opRemove). Notification, as we've already seen above, is a iterative call and it's a virtual method.
So the additionally unnecessary performance hit gets you twice. Once on creation and once on destruction. This performance hit can be completely avoided by passing nil as the parameter to our TTable instance that is locally created and freed.
- When informed of this performance hit, it is sometimes suggested to use Application as the owner instead of Self (in the original code example "Self" was a TForm). This suggestion was based on the fact that statistically, the Application object will tend to own fewer components (e.g., just the auto-create forms), than a form would. While this may be true, passing the Application object as the owner actually has an even more severe impact on performance, because the Notification call iterates through each component owned by the owner. This means that every component on every form owned by the Application object will get it's Notification method called, in addition to every TComponent descendant owned by the Application directly.
When you specify an owner in the constructor of a TComponent, the owner's internal component list gains a new reference to your component. If the owner does not own any components, the list does not yet exist and is subsequently created. The amount of memory consumed here could easily be called negligible, but it is unnecessary all the same.
Notification is used for a number of purposes. One of these uses is auto-hookup. Auto-hookup occurs when a component that needs to link to another component overrides the Notification method, looking for a component of that type. When it's found (assuming the linked property is not already set), it connects automatically. A number of third-party component packages have these. Here's a variation of auto-hookup from the VCL (taken from DBTables.pas):
procedure TSession.Notification(AComponent: TComponent; Operation: TOperation) ;
begin
  inherited Notification(AComponent, Operation) ;
  if AutoSessionName and (Operation = opInsert) then
    if AComponent is TDBDataSet then
      TDBDataSet(AComponent).FSessionName := Self.SessionName
    else if AComponent is TDatabase then
      TDatabase(AComponent).FSession := Self;
end;
When "Self" is the owner, Delphi will pass your dynamically created component to every other component on the form, unnecessarily exposing your component to unexpected third-party auto-hookup code. This code can change your component's properties and assign handlers to it's events.
This code also introduces the risk of Delphi attempting to free the dynamically-created instance twice (this is a bad thing, and can result in AVs and other problems). This will happen if the owner is freed within the Try/Finally block.
This could happen unexpectedly if the code inside the Try block was time-intensive, and a method within the block directly or indirectly called Application.ProcessMessages.
If this condition existed, and the user closed the form while execution was in the Try block, then the form would be destroyed in the call to Application.ProcessMessages. When the form is destroyed, it also destroys all owned components (including the TTable). At this point in the execution, the reference created by "TTable.Create" is invalid. Any further references to that TTable object (e.g., calling methods or setting properties of TTable) would most likely result in access violations, as would the final call to Free. If this were to occur, it would be a problem that would be extremely difficult to find, debug, and ultimately trace back to the fact that you should have passed nil in as the owner to the dynamically created and locally-used TTable.
Summary
If you want to dynamically create a component and explicitly free it sometime later, always pass nil as the owner. Failure to do so can introduce unnecessary risk, as well as performance and code maintenance problems.
SHARE