Managing Object LifecycleΒΆ

As discussed in Object Lifecycle, Q2Pix uses reference counting for managing the lifecycle of objects, and the client code must follow just a simple rule:

Q2Pix Reference Counting Rule

Every object reference returned either as the result of a method, or through a method parameter, must be released with a call to ImgObj_Release when no longer needed.


Let’s see how this rule is applied to the first scenario presented in that chapter:

  • A form method creates an Image Document object.
  • The form method assigns the object to an Image Area.
  • The form contains buttons that allow the user to rotate the image right or left in the area.

The form method would look like this:

   // =============================================
   // ImageViewer form method

Case of
   : (Form event=On Load)

      C_TEXT($docRef)
      $docRef:=ImgDoc_CreateWithFile ("/path/to/image.tiff")

      ImgArea_SetDocument (xImgArea;$docRef)

      ImgObj_Release ($docRef)

End case

First, the form method creates an Image Document reference from an image file on disk with a call to the ImgDoc_CreateWithFile method. At this point the document’s retain count is 1.

Then the document reference is assigned to plug-in area xImgArea with a call to ImgArea_SetDocument. The plug-in area retains the document reference, because it is needed for displaying. At this point the document’s retain count is incremented by one and becomes 2.

Finally the form method releases the document reference because it is no longer needed. Now the document’s retain count is decremented by one, and becomes 1.

Next let’s have a look how the "RotateImageRight" button in the viewer form would be coded:

   // =============================================
   // ImageViewer.RotateImageRight object method

C_TEXT($docRef)
$docRef:=ImgArea_GetDocument (xImgArea)

C_LONGINT($frameIdx)
$frameIdx:=ImgArea_GetFrame (xImgArea)

RasterFrame_RotOrientationLeft ($docRef;$frameIdx)

ImgObj_Release ($docRef)

After the execution of the On Load form method in the previous example, the retain count of the document assigned to the area is 1.

The button’s method first retrieves the document reference from the plug-in area with a call to ImgArea_GetDocument. The plug-in area returns the document reference with its retain count incremented by one, which now equals 2.

Then the object reference is passed to RasterFrame_RotOrientationLeft to do the orientation transformation.

Finally, the button method releases the document reference because it is no longer needed. The document’s retain count is decreased by one, dropping to 1 again.

When the form window is closed and the plug-in area is destroyed, or when another document is assigned to the area, the area will release its current document. The document’s retain count will decrease by one becoming 0, and the document will be destroyed.


Let’s see how the second scenario presented in that chapter would be coded:

  • A project method attached to a menu item presents the standard file selection dialog allowing the user to select an image file.
  • After the user selects a file, the project method verifies that the selected file is in a readable format by creating an Image Document object with the selected file.
  • If the verification step succeeds, the project method passes the image document reference to a new process for processing or display.

While this seems to be straightforward to implement, just pass the document object to the new process and let it retain the reference for its own use, it is a bit tricky due to the nature of 4D’s multi-process runtime environment.

First, let’s examine the simplistic/naive approach to this problem.

The menu method would look like this:

   // =============================================
   // MENU_SELECT_IMAGE project method.
   // *** Warning: WRONG implementation***

C_TEXT($imagePath)
$imagePath:=ImgDlog_PromptOpenFile ("Select an image")
If ($imagePath#"")

   C_TEXT($imgDocRef)
   $imgDocRef:=ImgDoc_CreateWithFile ($imagePath)   // Create the document reference.
   If ($imgDocRef#"")   // This is a supported image

      C_LONGINT($handlerProcess)
         // Pass the document reference to the new process as a parameter.
      $handlerProcess:=New process("HANDLE_IMAGE";1024*1024;\
         "$HANDLE_IMAGE";$imgDocRef)

      ImgObj_Release ($imgDocRef)   // Release the reference we created.

   End if

End if
   // =============================================
   // HANDLE_IMAGE project method.
   // *** Warning: WRONG implementation***

C_TEXT($1;$imgDocRef)
$imgDocRef:=$1

   // Retain the document reference to keep it around.
$imgDocRef:=ImgObj_Retain ($1)

   // ...
   // Work with the image
   // ...

ImgObj_Release ($imgDocRef)   // Release the document reference.

The problem with this approach is the following: it is not guaranteed by 4D’s runtime that between the calls to New process and ImgObj_Release in the MENU_SELECT_IMAGE method, the new process implemented by the HANDLE_IMAGE method has got a chance to run and execute the ImgObj_Retain call to keep the document reference alive for its own use.

In other words, there is a good chance that the call sequence would look like this:

  • MENU_SELECT_IMAGE : ImgDoc_CreateWithFile -> the document’s retain count is 1.
  • MENU_SELECT_IMAGE : New process -> the document’s retain count is still 1.
  • MENU_SELECT_IMAGE : ImgObj_Release -> the document’s retain count becomes 0 and it is destroyed.
  • HANDLE_IMAGE : ImgObj_Retain -> failure: the document has already been destroyed.
  • HANDLE_IMAGE : ...failure to work with the image document.
  • HANDLE_IMAGE : ImgObj_Release -> failure: the document has already been destroyed.

instead of the expected/desired sequence:

  • MENU_SELECT_IMAGE : ImgDoc_CreateWithFile -> the document’s retain count is 1.
  • MENU_SELECT_IMAGE : New process -> the document’s retain count is still 1.
  • HANDLE_IMAGE : ImgObj_Retain -> the document’s becomes 2.
  • MENU_SELECT_IMAGE : ImgObj_Release -> the document’s retain count becomes 1.
  • HANDLE_IMAGE : ...work with the image document.
  • HANDLE_IMAGE : ImgObj_Release -> the document’s retain count becomes 0 and it is destroyed.

The right way to implement this scenario is to ensure the correct call execution order by using some sort of synchronization:

   // =============================================
   // MENU_SELECT_IMAGE project method.

C_TEXT($imagePath)
$imagePath:=ImgDlog_PromptOpenFile ("Select an image")
If ($imagePath#"")

   C_TEXT($imgDocRef)
   $imgDocRef:=ImgDoc_CreateWithFile ($imagePath)   // Create the document reference.
   If ($imgDocRef#"")   // This is a supported image

         // Use a process variable as a signal
      C_BOOLEAN(gGotTheImage)
      gGotTheImage:=False

      C_LONGINT($handlerProcess)
         // Pass the document reference to the new process as a parameter.
      $handlerProcess:=New process("HANDLE_IMAGE";1024*1024;"$HANDLE_IMAGE";$imgDocRef)

         // Wait until $handlerProcess signals us (or dies)
      C_BOOLEAN($done;$handlerDead)
      Repeat
         GET PROCESS VARIABLE($handlerProcess;gGotTheImage;gGotTheImage)
         $handlerDead:=(Process state($handlerProcess)<0)
      Until (gGotTheImage | $handlerDead)

      ImgObj_Release ($imgDocRef)   // Release the reference we created.

   End if

End if
   // =============================================
   // HANDLE_IMAGE project method.

C_TEXT($1;$imgDocRef)

$imgDocRef:=ImgObj_Retain ($1)   // Grab a reference to the image document

C_BOOLEAN(gGotTheImage)
gGotTheImage:=True   // Signal the caller

   // ...
   // Work with the image
   // ...

ImgObj_Release ($imgDocRef)   // Release the reference to the image document

Here the two methods agree upon a protocol to coordinate the call sequence: a process variable that is used as a signal.

The MENU_SELECT_IMAGE method waits after the New process call for the new process to signal before continuing execution and calling ImgObj_Release.

The HANDLE_IMAGE method first retains the document reference it receives as a parameter, and then signals the caller method via process variable. This ensures the correct call sequence.

Finally, when HANDLE_IMAGE is done the document reference is released and the semaphore is destroyed before ending the process.


So, the typical pattern when working with Q2Pix objects is:

  • Create an object with some plug-in or component wrapper method.
  • Work with the object.
  • Release the object when no longer needed.

Or, when some entity (method, module, or process) receives an existing object reference instead of creating a new one:

  • Retain the object.
  • Work with the object.
  • Release the object when no longer needed.

When passing object references between processes, there must be some sort of synchronization between the participating processes in order to retain/release object references correctly. A simple way to implement such synchronization is with the use of a process variable as described above.