Introduction to Eo: Creating and Destroying Objects

EFL is written in C and therefore lacks language support for Object-oriented programming. The Eo generic object system was designed to provide such capabilities to EFL. Eo objects are at the core of almost every EFL entity (like Windows, Buttons or Timers) providing lifecycle management and inheritance abilities amongst other features.

This tutorial shows you the basics of creating and destroying Eo objects as well as Reference Counting, the technique at the heart of the Eo object lifecycle management.

This tutorial is more theoretical than practical however the concepts being explained are crucial, so it's highly advisable to go through it. If you're familiar with Reference Counting, you should have no trouble at all.

Prerequisites

Step One: Object Creation and Destruction

Start with the basic EFL application skeleton from the Hello World tutorial and add some placeholder methods that will be filled later on:

#define EFL_BETA_API_SUPPORT 1
 
#include <Eina.h>
#include <Efl_Core.h>
 
// Create our test hierarchy
static void
_obj_create()
{
}
 
// Destroy the test hierarchy
static void
_obj_destroy()
{
}
 
EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
   // Create all objects
   _obj_create();
 
   // Destroy all objects
   _obj_destroy();
 
   // Exit
   efl_exit(0);
}
EFL_MAIN()

Sensibly enough the objects are created in _obj_create() and destroyed in _obj_destroy().

Also define some global pointers to keep track of the created objects. In a real application you won't be using global variables but do so in this tutorial for the sake of simplicity. Place this line after the includes:

Eo *_root;

This tutorial will use generic Eo * pointers to store objects. Your programs can use these too or use more specific types like Efl_Ui_Win * or Efl_Ui_Button *. EFL methods will accept both types (generic and specific) and perform runtime checks to ensure that you provide objects of the type expected by a method.

Next fill-in the object creation method:

static void
_obj_create()
{
   // First create a root element
   _root = efl_new(EFL_MODEL_ITEM_CLASS,
                   efl_name_set(efl_added, "Root"));
}

efl_new() is the most basic of the object creation methods. It does three things:

  • Creates a new object of the type (class) specified in the first parameter and returns a pointer to it.
  • Calls any constructor method defined for the object's class.
  • Calls a list of methods to further initialize or configure the new object.

In the code snippet above an object of type EFL_MODEL_ITEM_CLASS is created, and efl_name_set() is then used to configure the object (as explained below).

Note that the specific type of object being created in this tutorial (EFL_MODEL_ITEM_CLASS) is not important. It was chosen because it does not need configuration and is therefore easier to use.

You can use as many configuration calls inside efl_new() as you need, since it accepts an infinite number of parameters. Also, configuration calls can use the special symbol efl_added which refers to the object being created. Together these two powerful features make object creation code much more compact.

In this example, efl_name_set() is used to name the new object "Root" (note the efl_added parameter being used).

The return value of efl_new() is the new object with type Eo *, which you can safely assign to a pointer of the specific type you requested or keep it as the generic Eo *. In this case the pointer is stored in the _root variable for later use.

At this point you have created your first Eo object. It is now time to decide who will be responsible for destroying it later. If the object is not destroyed system resources will eventually be exhausted. This is known as a memory leak.

Reference Counting

In the simplest case when only one piece of code is interacting with an object, you can create the object, use it and then destroy it. In more complex scenarios where different parts of code use the same object, it's not easy to know when an object isn't in use anymore and can therefore be safely destroyed.

A common approach to this problem is to use the Reference Counting technique whereby every object keeps track of how many people (pieces of code) are using it, using an internal reference counter (refcount for short):

  • efl_new() returns a new object with a reference counter of 1, meaning that the object is currently in use by one person: you, the caller of efl_new().
  • If somebody else wants to work with that object it first needs to obtain a reference by using a call like efl_ref() on the object. This increments the internal reference counter.
  • When that piece of code is done working with the object it returns the reference by calling efl_unref() on the object in question. This decrements the internal reference counter.

The advantage of this technique is that objects can automatically be destroyed when their internal reference counter reaches 0 as no one else is using them.

Back to the tutorial code, you obtained an object using efl_new() with a refcount of 1, therefore you need to return that reference at some point. It is time to fill-in the _obj_destroy() method:

static void
_obj_destroy()
{
   // Destroy the root element
   printf ("Deleting root...\n");
   efl_unref(_root);
}

The reference you were holding to the _root object has now returned. Since it was the only existing reference to this object, the internal reference counter will reach 0 and the object will be destroyed. This isn't immediately obvious but you will explore this process further in the next tutorial.

The first step of the tutorial is now complete. You may not have seen much on screen but you now understand the fundamental concept of Object Lifecycle Management: when objects are created and destroyed.

See below the complete listing, which you can build and run yourself:

#define EFL_BETA_API_SUPPORT 1
 
#include <Eina.h>
#include <Efl_Core.h>
 
#include <stdlib.h>
 
Eo *_root;
 
// Create our test hierarchy
static void
_obj_create()
{
   // First create a root element
   _root = efl_new(EFL_MODEL_ITEM_CLASS,
                   efl_name_set(efl_added, "Root"));
}
 
// Destroy the test hierarchy
static void
_obj_destroy()
{
   // Destroy the root element
   printf ("Deleting root...\n");
   efl_unref(_root);
}
 
EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
   // Create all objects
   _obj_create();
 
   // Destroy all objects
   _obj_destroy();
 
   // Exit
   efl_exit(0);
}
EFL_MAIN()

Step Two: A More Complex Hierarchy

In this second section more objects will be created as children of other objects, forming a hierarchy. This will give you more hands-on training with the concepts you acquired in the previous section.

Start by adding two more global object pointers to keep track of the new objects. Just below the #includes, replace the Eo *_root; line with:

Eo *_root, *_child1, *_child2;

Next, in the _obj_create() method add these lines below the call to efl_new():

   // Create the first child element
   _child1 = efl_add(EFL_MODEL_ITEM_CLASS, _root,
                     efl_name_set(efl_added, "Child1"));

efl_add() is one of the most commonly-used methods in EFL so it requires careful inspection. It works the same way efl_new() does (it creates and initializes a new object), and it then gives the new object a parent. This means that the only reference to the new object belongs to the parent, so you don't need to worry about returning it. It also means that you won't be able to work with this object later on unless you obtain an extra reference. In fact, you don't event need to keep the _child1 pointer. It exists because you will be using it in the following tutorial.

As you can see, efl_add() is very convenient since it allows you to create an object, configure it and add it to a hierarchy without even requiring a variable to store it.

NOTE:

Parenthood does not need to be permanent in EFL: you can always remove a child object from its parent using efl_parent_set(obj, NULL). Be careful, though, because this returns to you the reference the parent was holding. You are now responsible for returning this reference, and failing to do so will leak the object. If you want to remove the object from its parent and return the reference at the same time, you can use the convenience method efl_del().

Likewise, you can assign an object to a parent using efl_parent_set(obj, parent). This steals the reference from you and gives it to the new parent.

In the above code snippet, you are creating a new object (of type EFL_MODEL_ITEM_CLASS, again) and setting its parent to _root, so from this point onwards you can forget about this object: its parent will take care of it.

Now add a second object immediately below the previous one:

   // Create the second child element, this time, with an extra reference
   _child2 = efl_add_ref(EFL_MODEL_ITEM_CLASS, _root,
                         efl_name_set(efl_added, "Child2"));

This time you didn't use efl_add() but efl_add_ref(). This method creates objects with an initial reference count of 2, one reference for the parent and one for you. This is handy when you want the object to have a parent but also want to work with it. You will need to return your extra reference later on, and the parent will return its own reference.

NOTE:

efl_add_ref() accepts NULL as parent. In this case, it behaves exactly like efl_new() and returns an object with a single reference.

efl_add() does not accept NULL as parent and will show an error message at runtime if you try.

In this simple tutorial you will not be doing anything special with _child2: It has been created with an extra reference for example purposes only.

Next, move on to the _obj_destroy() method. You need to return the extra reference to _child2, immediately below the previous call to efl_unref():

   // Destroy the child2 element, for which we were keeping an extra reference
   printf ("Deleting Child2...\n");
   efl_unref(_child2);

Note how you are not returning the reference to _child1. This is because that reference belongs to its parent, _root, which handles it. In this example when _root is destroyed it will also return the references for all its children. This in turn destroys _child1 because there was only one reference to it but not _child2 because there is an extra reference to it. You will manually return this with an explicit call to efl_unref().

If you compile and run the complete code below you will only see messages about objects being deleted but in so doing you've learned about the very important topic of object creation and destruction, as well as how to avoid memory leaks.

#define EFL_BETA_API_SUPPORT 1
 
#include <Eina.h>
#include <Efl_Core.h>
 
#include <stdlib.h>
 
Eo *_root, *_child1, *_child2;
 
// Create our test hierarchy
static void
_obj_create()
{
   // First create a root element
   _root = efl_new(EFL_MODEL_ITEM_CLASS,
                   efl_name_set(efl_added, "Root"));
 
   // Create the first child element
   _child1 = efl_add(EFL_MODEL_ITEM_CLASS, _root,
                     efl_name_set(efl_added, "Child1"));
 
   // Create the second child element, this time, with an extra reference
   _child2 = efl_add_ref(EFL_MODEL_ITEM_CLASS, _root,
                         efl_name_set(efl_added, "Child2"));
}
 
// Destroy the test hierarchy
static void
_obj_destroy()
{
   // Destroy the root element
   printf ("Deleting root...\n");
   efl_unref(_root);
 
   // Destroy the child2 element, for which we were keeping an extra reference
   printf ("Deleting Child2...\n");
   efl_unref(_child2);
}
 
EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
   // Create all objects
   _obj_create();
 
   // Destroy all objects
   _obj_destroy();
 
   // Exit
   efl_exit(0);
}
EFL_MAIN()

Summary

At the end of this tutorial you have learned:

  • Standalone (parent-less) objects are created with efl_new(). You must manually destroy these objects using efl_unref().
  • efl_add() creates objects which belong to their parent. You don't have to do anything to destroy the objects.
  • Objects can be created with an extra reference with efl_add_ref(), which is useful if you want to give the object a parent and also start working with it right away. You must return the extra reference using efl_unref().
  • The parent of an object can be changed at any time using efl_parent_set(). Be careful with object ownership if you do so.

The following tutorial builds on top of this one, adding instrumentation calls to display the actual values of the different reference counters.

Further Reading

Reference Counting in Eo
Next tutorial in this series
Setting up the Development Environment
Read this before trying to develop with the EFL
Hello World tutorial
Teaches the basic EFL application skeleton