WWW-www.enlightenment.org pushed a commit to branch master. http://git.enlightenment.org/website/www-content.git/commit/?id=5f68f4fff9e42f3be019f1abcfa72315b5eee3c0
commit 5f68f4fff9e42f3be019f1abcfa72315b5eee3c0 Author: Xavi Artigas <[email protected]> Date: Wed Nov 8 06:34:20 2017 -0800 Wiki page eo-intro.md changed with summary [created] by Xavi Artigas --- pages/develop/tutorial/c/eo-intro.md.txt | 289 +++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) diff --git a/pages/develop/tutorial/c/eo-intro.md.txt b/pages/develop/tutorial/c/eo-intro.md.txt new file mode 100644 index 00000000..072ad6ab --- /dev/null +++ b/pages/develop/tutorial/c/eo-intro.md.txt @@ -0,0 +1,289 @@ +--- +~~Title: Introduction to Eo~~ +--- + +# Introduction to Eo: Creating and destroying objects # + +The Eo generic object system was designed to provide **Object-Oriented capabilities** to the EFL. Eo objects are at the core of almost every EFL entity (like Windows, Buttons or Timers), providing lifecycle management and inheritance abilities, for example. + +This tutorial will show you the basics of creating and destroying Eo objects, along with **Reference Counting**, the technique at the heart of the Eo object lifecycle management. + +Due to its fundamental nature, this tutorial is more theoretic than the average. The concepts being explained are crucial, though, so its reading is highly encouraged. If you are familiar with Reference Counting, it should be a breeze. + +## Prerequisites ## + +* Read the [Setting up the Development Environment](/develop/setup/c/) guide so you are able to build and run EFL applications. +* Read the [Hello World tutorial](hello-word.md) so you know the basics of building an EFL application. + +## Step One: Object Creation and Destruction ## + +Start with the basic EFL application skeleton from the [Hello World tutorial](hello-word.md) and add some placeholder methods that will be filled later on: + +```c +#define EFL_EO_API_SUPPORT 1 +#define EFL_BETA_API_SUPPORT 1 + +#include <Eina.h> +#include <Efl.h> +#include <Elementary.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 will be created in ``_obj_create()``, and will be 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 they help keep these first tutorials as simple as possible. Put this line after the includes: + +```c +Eo *_root; +``` + +This tutorial will use generic ``Eo *`` pointers to store the objects; your programs can use them 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 a method is expecting. + +Now, fill-in the object creation method: + +```c +static void +_obj_create() +{ + // First create a root element + _root = efl_add(EFL_MODEL_ITEM_CLASS, NULL, + efl_name_set(efl_added, "Root")); +} +``` + +``efl_add()`` is one of the most commonly-used methods in EFL so it requires careful inspection. It mainly does **three** separate things: + +* Creates a new object of the type (**class**) specified in the first parameter. +* Sets the new object as a child of the already existing object specified in the second parameter. +* 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 being created, set as a child of no one (the ``NULL`` parameter) and ``efl_name_set()`` is used to configure the object (as explained below). + +The particular kind of object being created in this tutorial (``EFL_MODEL_ITEM_CLASS``) is irrelevant. It was chosen because it does not need configuration and is therefore easier to use. + +You can use as many configuration calls inside ``efl_add()`` 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 smaller and more compact: You could create an object, configure it and add it to a scene without even requiring a variable to store it! + +In this example, ``efl_name_set()`` is used to name the new object "Root" (note the ``efl_added`` parameter being used). + +Finally, the return value of ``efl_add()`` is the new object, with type ``Eo *`` which you can safely assign to a pointer of the specific type you requested, or keep the generic ``Eo *``, as you prefer. 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 time now to talk about who will be responsible for destroying it later on. Because, 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 though, when different parts of the code use the same object, maybe from different execution threads, it is not easy to know when an object is not in use anymore and can therefore be safely destroyed. + +A common approach to this problem is to use the **Reference Counting** technique, in which every object keeps track of how many people (pieces of code) are using it in an internal *reference counter*: + +* When somebody wants to work with a particular object it first needs to *get a reference to it* by using a call like ``efl_ref()`` on the object. This increases 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. This decreases the internal reference counter. + +The advantage of this technique is that objects can be automatically destroyed when their internal reference counter reaches 0, because it means that nobody is using them anymore. + +### Reference Counting and ``efl_add()`` ### + +Eo objects created through ``efl_add()`` have a starting reference count of 1, meaning that there is one piece of code using them. **It is very important to understand which one is this piece of code**, because it will be responsible for returning the reference. It is easy, though: + +* **If you gave the object a parent**: Then that parent is the owner of the reference. There is nothing else that you need to do with the object. You cannot actually work with the object, because you do not hold any reference to it (more about this later). +* **If you gave no parent to the object**: If you passed ``NULL`` as the parent, then **you** are the owner of the reference and you are responsible for returning it with ``efl_unref()``. Forgetting to do so is the most common cause of memory leaks. + +Back to the tutorial code, no parent was given to the object created in ``_obj_create()``, therefore you need to return that reference at some point. It is time to fill-in the ``_obj_destroy()`` method: + +```c +static void +_obj_destroy() +{ + // Destroy the root element + printf ("Deleting root...\n"); + efl_unref(_root); +} +``` + +As you can see, the reference you were holding to the ``_root`` object is returned. Since it was the only existing reference to this object, the internal reference counter will reach 0 and the object will be destroyed. Right now you have no proof of that, but the following tutorial will show you what is happening behind the scenes. + +With this, the first step of this tutorial is complete. It does not show much on screen, but it was necessary to explain the fundamental concept of **Object Lifecycle Management**: When are objects created and when are they destroyed. + +Here you have the complete listing, which you can build and run: + +```c +#define EFL_EO_API_SUPPORT 1 +#define EFL_BETA_API_SUPPORT 1 + +#include <Eina.h> +#include <Efl.h> +#include <Elementary.h> + +#include <stdlib.h> + +Eo *_root; + +// Create our test hierarchy +static void +_obj_create() +{ + // First create a root element + _root = efl_add(EFL_MODEL_ITEM_CLASS, NULL, + 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 step more objects will be added forming a hierarchy. This will give you more hands-on training with the concepts you acquired in the previous step. + +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: + +```c +Eo *_root, *_child1, *_child2; +``` + +And now, in the ``_obj_create()`` method, add a new ``efl_add()`` line below the previous one: + +```c + // Create the first child element + _child1 = efl_add(EFL_MODEL_ITEM_CLASS, _root, + efl_name_set(efl_added, "Child1")); +``` + +Here you are creating a new object (of type ``EFL_MODEL_ITEM_CLASS``, again) and setting its parent to ``_root``. As you saw in the previous step, the one reference to the new object belongs to the parent, therefore you need not worry about returning it. It also means that you won't be able to work with this object later on. In fact, you don't event need to keep the ``_child1`` pointer (it's here because you will be using it in the following tutorial). + +Add now a second object just below the previous one: + +```c + // 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 another one for you. This is handy when you want the object to have a parent but you also want to work with it. Obviously, you will need to return the extra reference later on. + +In this simple tutorial you will not be doing anything special with ``_child2``, it has been created with an extra reference for illustration purposes only. + +Move now then to the ``_obj_destroy()`` method. You need to return the extra reference to ``_child2`` there, right below the previous call to ``efl_unref()``: + +```c + // 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 takes care of it. In this example, when ``_root`` is destroyed it will also return the references for all its children. In turn, this destroys ``_child1`` (because there was only one reference to it) but **not** ``_child2`` (because there is an extra reference to it, which we will manually return with the explicit call to ``efl_unref()``). + +And with that, this tutorial is complete. If you compile and run the complete code below you will only see messages about objects being deleted, but, in the process, you have learned about the very important topic of object creation and destruction, and how to avoid memory leaks. + +```c +#define EFL_EO_API_SUPPORT 1 +#define EFL_BETA_API_SUPPORT 1 + +#include <Eina.h> +#include <Efl.h> +#include <Elementary.h> + +#include <stdlib.h> + +Eo *_root, *_child1, *_child2; + +// Create our test hierarchy +static void +_obj_create() +{ + // First create a root element + _root = efl_add(EFL_MODEL_ITEM_CLASS, NULL, + 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: + +* Objects are **created** with ``efl_add()``. +* ``efl_add()`` creates objects with **one reference**, which belongs to their parent. You don't have to do anything to destroy the objects. +* If you do not provide a parent to ``efl_add()`` then the reference belongs to **you**, and you have to return it when you are done working with the object using ``efl_unref()``. + +## Further Reading ## + +[The following tutorial](eo-intro-2.md) builds on top of this one, adding instrumentation calls to display the actual values of the different reference counters. \ No newline at end of file --
