Tizen Widget探究(C的对象化编程)

EFL - 1.7.1

 

Void指针
    void *则为“无类型指针”,可以指向任何数据类型
    void指针不能复引用,因为void指针只知道,指向变量/对象的起始地址
    –*vp//错误

    void指针不能参与指针运算,除非进行转换
    有时候由于重载等的干扰,导致需要转换成void *,来进行取地址
    –例如,(void *)obj.member,就可以取到member的地址;直接&(obj.member)取到的实际上是obj的开始地址

EFL 用了大量的void指针,
信息隐藏
–别人看到的是void指针,无法知道我们其指向的地址保存的是什么东西

风险
–允许把任何类型的参数都传入以void*指针类型为参数的函数

 

void*指针在EFL的应用举例

1.保存一个指向任何类型的对象的指针(key-value)

void evas_object_data_set(
                                    Evas_Object *obj,  //
                                    const char *key,    //
                                    const void *data    //
                                )

 

2.获取这个对象

void evas_object_data_get(
                                 const Evas_Object *obj,
                                 const char *key
                                )

 

 

typedef struct appdata{
Evas_Object* win;
Evas_Object* layout;
Evas_Object* conform;
Evas_Object* nf;
Evas_Object* base_ly;
Evas_Object* genlist;
Elm_Object_Item *nf_it;
} appdata_s;

在某个文件某个函数中我只知道appdata_s的nf这个对象,但是我想通知genlist这个对象去

运行某个东西或者其他的,怎么办呢?先set好,之后就可以:

Evas_Object *genlist = evas_object_data_get(cv_data->navi, "main_list");

 

 

Const

  • const int* p;

–      其实等价于const int (*p); 和 int const (*p);

–      即,*p是常量。p指向的数据不能修改。但是,p本身的值可以修改(P可以指向其他的地址)

  • int* const p;

–      这个指针是常量,不能修改,但是可以修改指针指向的值

 

构造函数和析构函数

  • C++

–      该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数->由构造函数完成成员的初始化工作

–      如果用new运算符动态地建立了一个对象,当用delete运算符释放该对象时,先调用该对象的析构函数

  • C里面没有new, C++才有new。

–      C里面是malloc和free,但是这个不能建立类的对象,因为不能调用构造函数和析构函数

  • C的面向对象编程,我们自己编写函数代替构造函数和析构函数

–      Tizen:xxxx_add() 和 xxxx_del()

 

Overriding & overloading

  • overriding:

–      父类与子类,相同的名称和参数

  • overloading:

–      同一个类,不同的参数个数或者参数类型,返回值可以改变。

 

C实现继承

  • 使用结构struct来实现
  • 要继承的基类必须作为子类的成员放在子类结构的第一位
typedef struct son
{ 
    father base;//基类
    int version;
}

 

为什么呢?

因为我们要强制类型转换,呵呵呵

 

Widget:smart_class

 

 

譬如有个void指针data指向上面的数据结构

那么我们可以用强制类型转换:

Evas_Smart_Class *sc = (Evas_Smart_Class *)data; //能获取a,b,c,d
Elm_Widget_Smart_Class *wsc = (Elm_Widget_Smart_Class *) data; //获取更多的,e,f,g,h,i,j,
Eml_MyWidget_class *mwsc = (Eml_MyWidget_class *)data; //获取更多的 , k,l,m,n,o,p

 

typedef struct _Elm_Genlist_Smart_Class
{
       Elm_Layout_Smart_Class base;//基类
       int  version;   
} Elm_Genlist_Smart_Class;

 

 

1.继承Layout类

2.实现scrollable接口

3.滑动的时候,动态产生和销毁items[节省内存,减低消耗]

 

 

 

Immediate mode Rendering

  • 原始的canvas绘制,简单
  • 即时绘画
  • Graphics Library不知道自己都画了些什么

–      当需要重画的时候,需要再重新需要由application层再从零开始重新画(工作重复,繁琐)

 

 

 

Retained mode Rendering

  • 状态机
  • Graphic Library带有一个完善的model来保存需要显示的信息。

–      譬如:Tizen上面的所有widget实例都对应一个Evas_object来保存widget的相关信息

  • Application只需要创建widget,widget的显示以及update,统一由Graphic Library来维护

 

 

 

如何区别

  • Graphics Library是否有独立的model保存需要画的Object信息。

–      Tizen有Evas ---->retained mode

 

  • 需要更新时,谁来处理优化工作。

–      Tizen---->由Evas来统一优化处理

 

Evas(Evas 是Retained mode Rendering )

 

Evas介绍

  • Evas是一个canvas display library
  • Evas不是widget,也不是widget toolkit,但是是widget的基础
  • 在Evas的世界里,canvas是结构化的,在canvas上面是严格结构化的Evas_Object
  • 在Evas的世界里,你可以把Evas当做是一个状态机

 

Evas_object To Widget

  • 每个在手机屏幕上面存在的widget实例都存在一个相对应的Evas_object实例

–      一对一关系,用来记录和跟踪widget实例的状态属性等

–      Evas_object随着widget实例的构建而构建,随着析构而消逝(回收)

–      Evas_object不是widget的一部分

 

Evas_object组成树状结构

 

 

 

对于Widget来说,每个Evas_object都是widget

可能是Button,可能是layout,可能是entry,可能是genlist等等

 

类结构

  • 类是两个结构体的组合

–      一个是实例结构体,另一个是类结构体

  • 类结构体初始化函数一般被调用一次,而实例结构体的初始化函数的调用次数等于对象实例化的次数。

–      所有实例共享的数据,可保存在类结构体中(函数),而所有对象私有的数据,则保存在实例结构体中

 

 

 

 

后面我们会看到:

obj->smart.smart = s;  设置smart class  (s为3中函数的参数)

application

而evas_object_smart_data_set 是设置object_data->data, 就是说明我们从object->object_data就可以找到data了

o = (Evas_Object_Smart *)(obj->object_data);

o->data = data; //看清楚了,是object_data里面的void* 指针成员 data指向widget的data

这意味着Evas_object都bind着一个widget层的data,而这个data

就是widget user设计的。

 

Widget类的实例化

l  Smart Class 初始化介绍

上面说到每个widget类一般都有个Smart Class结构

l  Smart Data 初始化介绍

上面说到每个widget类一般都有个Smart Data结构

 

Smart class构造函数

xxxx_add()

  调用

obj = elm_widget_add( _elm_layout_widget_smart_class_new() , parent);

  • 注意了,这里的_elm_layout_widget_smart_class_new()的确是个函数,

       在evas.h搜宏

                                      EVAS_SMART_SUBCLASS_NEW

        可以找到这个函数

  • smart = evas_smart_class_new(sc);
  •    别看这里那么多代码,其实就仅仅是准备了smart class ,没错,千辛万苦就仅仅是准备了smart class作为返回值

 

Smart class的统一宏定义

  • 在Evas.h中统一定义了一个宏,方便创建新Widget 的Smart Class:

EVAS_SMART_SUBCLASS_NEW

  • 主要作用:

–      定义初始化子类的Smart Class的new 和 set函

  • New函数会通过set函数先获取到该控件所有父类的smart class并且继承过来,然后new一个smart class
  • Set函数用于设置一个smart  class(嵌套调用来获取父类的smart class)

 

主要作用:

定义两个函数( prefix##_ 为前缀,如layout就会替换为:_elm_layout_widget):

static void prefix##_smart_set(api_type * api)

static Evas_Smart *prefix##_smart_class_new(void)

调用函数:prefix##_smart_set_user(api)

如Elm_layout.c的

static void _elm_layout_smart_set_user(Elm_Layout_Smart_Class *sc)

注:这个宏实在太隐晦,而是使用了prefix##这样的东东实在让人无迹可寻。

当时我为了找一个函数:

               _elm_layout_widget_smart_class_new()

的实现我还在tizen论坛发问了。没有人回答我。

最后实在是没撤了,慢慢来找,一步一步推断,发现,竟然被定义在EVAS_SMART_SUBCLASS_NEW这个宏里面

#define EVAS_SMART_SUBCLASS_NEW(smart_name, prefix, api_type, parent_type, parent_func, cb_desc) \

  static const parent_type * prefix##_parent_sc = NULL;                                          \

  static void prefix##_smart_set_user(api_type * api);                                           \

  static void prefix##_smart_set(api_type * api)                                                 \

  {                                                                                              \

     Evas_Smart_Class *sc;                                                                       \

     if (!(sc = (Evas_Smart_Class *)api))                                                        \

       return;                                                                                   \

     if (!prefix##_parent_sc)                                                                    \

       prefix##_parent_sc = parent_func();                                                       \

     evas_smart_class_inherit(sc, prefix##_parent_sc);                                           \

     prefix##_smart_set_user(api);                                                               \

  }                                                                                              \

  static Evas_Smart *prefix##_smart_class_new(void)                                              \

  {                                                                                              \

     static Evas_Smart *smart = NULL;                                                            \

     static api_type api;                                                                        \

     if (!smart)                                                                                 \

       {                                                                                         \

          Evas_Smart_Class *sc = (Evas_Smart_Class *)&api;                                       \

          memset(&api, 0, sizeof(api_type));                                                     \

          sc->version = EVAS_SMART_CLASS_VERSION;                                                \

          sc->name = smart_name;                                                                 \

          sc->callbacks = cb_desc;                                                               \

          prefix##_smart_set(&api);                                                              \

          smart = evas_smart_class_new(sc);                                                      \

       }                                                                                         \

     return smart;                                                                               \

  }

 

Smart class申请

  • 当一个新类型的widget要加入的时候,会在Evas.h这个文件申请一个静态变量。

static api_type api;   ////api_type为widget的类型

静态变量生存期为整个源程序,是始终存在的。

这就保证了所有同类型的widget的smart class都是同一个smart class

 

构造总览

 

 

 

 

2.3.

初始化Smart Class

4

后面我们会看到是通过evas_object_smart_add() 函数再调用其他函数最后生成并初始化一个Smart Data

最后生成Evas_Object

 

Widget的初始化:class

 

 

 

obj = elm_widget_add(_elm_layout_widget_smart_class_new(), parent);

3.是参数调用

 

Widget的初始化:smart class准备

 

 

SC: smart class

这里是smart class的构造过程,在函数prefix##_smart_class_new内先定义一个临时变量,用这个临时变量完成了以下3.  5.,

然后再以这个临时变量为参数生成Evas_Smart

smart = evas_smart_class_new(临时变量);

最后把生成的smart  return回去作为参数值

obj = elm_widget_add(prefix##_smart_class_new() , parent);

1. prefix##_smart_class_new 是构造函数_add的参数调用

2.没错,就是定义在了Evas.h  用宏的方式

3.继承父类:其实就是把子类中的父类func pointer赋值为父类的默认值

5.调用子类的prefix##_smart_set_user(api);  根据子类需求,改变子类的的函数指针值赋予新函数

Evas is a clean display canvas API for several target display systems that can draw
anti-aliased text, smooth super and sub-sampled scaled images, alpha-blend objects
and much more.

 

Smart Class inheritance

  • 每个父类都提供一个_smart_class_get来给它的子类获取该父类的类方法
  • 该_smart_class_get函数会调用自己的smart_class_set函数
  • 子类在生成的时候,一定会调用父类的_smart_class_get函数
  • 通过不断的嵌套调用从而实现了继承
  • Smart_set函数中
  • prefix##_parent_sc = parent_func();
  • evas_smart_class_inherit(sc, prefix##_parent_sc);
  • prefix##_smart_set_user(api);
  • 这三个个语句是实现class继承的关键。
  • Prefix##_parent_sc 这个是是有统一宏定义的参数决定,都是每个widget的:XXX_smart_class_get函数
  • 而XXX_smart_class_get函数又会调用它自己的smart_set函数。
  • 然后smart_set函数又调用smart_get函数,一直到widget基类的_elm_widget_smart_set函数停止
  • prefix##_smart_set_user(api);这个函数是设置该类除了父类的之外自己的smart class
  • 自己set()->父亲get class->父亲set-》。。。。。。
  • 譬如:
  • Entry是layout的孩子,engry会先跑自己的_elm_entry_smart_set函数,在跑parent fun:layout的class get
  • elm_layout_smart_class_get()
  •   _elm_layout_smart_set(&_sc);
  •      elm_container_smart_class_get
  •         _elm_container_smart_set(&_sc);
  •             elm_widget_smart_class_get(void);
  •                _elm_widget_smart_set(&_sc);
  • 题外话:
  • #define ELM_WIDGET_SMART_CLASS_INIT_NAME_VERSION(name) \
  •   ELM_WIDGET_SMART_CLASS_INIT(EVAS_SMART_CLASS_INIT_NAME_VERSION(name))
  • 这个宏其实就是申请一个结构变量空间,
  • #define ELM_WIDGET_SMART_CLASS_INIT(smart_class_init)                        \
  •   {smart_class_init, ELM_WIDGET_SMART_CLASS_VERSION, NULL, NULL, NULL, NULL, \
  •    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
  • #define EVAS_SMART_CLASS_INIT_NAME_VERSION(name) {name, EVAS_SMART_CLASS_VERSION, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}

 

Init Widget(class && data)

 

 

1:elm_layout_add(Evas_Object *parent)

在app添加widget的时候调用elm_layout_add(),该函数作用是:

设置Smart class,Smart data,interface

2:

obj = elm_widget_add(_elm_layout_widget_smart_class_new(), parent);

3. evas_object_smart_add(e, smart);

2.3.4.5:

elm_layout_add()通过widget一直调用,最终会调用到Evas_object_smart.c的

evas_object_new ()来alloc一个Evas_Object对象

直接修改obj的属性,:obj->smart.smart = s;  设置smart class  (s为3中函数的参数)

Evas_object_smart_init()再初始化这个对象

6:

还会通过:

_evas_smart_class_ifaces_private_data_alloc()

来处理interfaces以及interface的parivate data的空间申请(如果不实现接口则没有)

7.

该函数最终会通过获取该子类的add方法来调用evas_object_smart_data_set() :

通过下面语句调用

if (s->smart_class->add) s->smart_class->add(obj);

在Elm_XXXX.c 或者 Elm_widget_XXXX.c 获取XXX_Smart_Data

调用XX_smart_add()

到这一步,实例化终于成功了。

 

Smart data的申请1

  • _smart_add()函数

上一张PPT我们看到,当smart class准备好了之后,会通过

7.

         if (s->smart_class->add)

                        s->smart_class->add(obj);

来调用_smart_add函数哦,没错,这个就是准备smart data的。

_smart_add()如何申请Smart data? 请看下一页

 

Smart data的申请2

  • 每个Widget都有自己的_smart_add()函数

都会使用宏: EVAS_SMART_DATA_ALLOC

  • _smart_add()函数的作用:

①     如果smart data没有申请的话就申请,并设置一些属性。

②     调用父类的_smart_add()函数,这样嵌套调用就可以设置属于父类data的属性了哦。

③     用面向对象的思想看待,这个其实就是Smart Data的构造函数

 

static void

_elm_genlist_smart_add(Evas_Object *obj)

{

   EVAS_SMART_DATA_ALLOC(obj, Elm_Genlist_Smart_Data);

   ELM_WIDGET_CLASS(_elm_genlist_parent_sc)->base.add(obj);

}

 * @remarks When writing a subclassable smart object, the @c .add() function

 *          needs to check if the smart private data is already allocated

 *          by some child object or not. This macro makes it easier to do it.

#define EVAS_SMART_DATA_ALLOC(o, priv_type)              \

  priv_type * priv;                                      \

  priv = evas_object_smart_data_get(o);                  \

  if (!priv) {                                           \

       priv = (priv_type *)calloc(1, sizeof(priv_type)); \

       if (!priv) return;                                \

       evas_object_smart_data_set(o, priv);              \

    }

 

EVAS_SMART_DATA_ALLOC介绍

l  这是一个宏

l  所有widget都会用到这个宏来申请smart data的空间(实例化)

l  首先检查这个widget的smart data有没有申请,如果没有申请,就alloc申请空间。

l  这个宏在子类smart_add()函数中配合使用,一般是使用这个宏之后再调用父类的smart_add()函数,总之就是设置smart data的。

  • 记得么,上文说过,如果smart data已经申请了,那么不会再申请了

 

#define EVAS_SMART_DATA_ALLOC(o, priv_type)              \

  priv_type * priv;                                      \

  priv = evas_object_smart_data_get(o);                  \

  if (!priv) {                                           \

       priv = (priv_type *)calloc(1, sizeof(priv_type)); \

       if (!priv) return;                                \

       evas_object_smart_data_set(o, priv);              \

    }

 

Smart Class的析构(1)

  • 每个控件都会有_smart_del函数

–      通过该函数清理自己的ex data,最后调用父类的_smart_del函数

Evas_object的析构只会在叶子控件进行:

–      调用evas_object_del(Evas_Object *obj)

 

Smart Class的析构(2)

  • evas_object_del(Evas_Object *obj)清理evas_object 的时候,会delete smart class。
  • 疑问:为什么在一开始就可以直接delete smart class。

–      答:上文说到,其实相同的控件不会独立申请空间去存储smart class ,都是引用同一个smart class。Delete的时候,只不过是去掉了对该smart class的引用。

–      那么真正删除smart class是什么时候呢?请看下文。

 

Smart class的引用计数

  • Smart class有一个int变量专门管理引用计数

    int usage;

  • 控件构建的时候

s->usage++;

  • 控件析构的时候

s->usage--;

 if ((s->usage <= 0) && (s->delete_me)) evas_smart_free(s);

 

控件构建时:

elm_widget_add(Evas_Smart *smart, Evas_Object *parent)

    evas_object_smart_add(Evas *e, Evas_Smart *s)

        evas_object_smart_use(s);

        {

            s->usage++;

         }

控件析构时:

evas_object_del(Evas_Object *obj)

   evas_object_smart_del(Evas_Object *obj)

      evas_object_smart_unuse(Evas_Smart *s)

      {

          s->usage--;

          if ((s->usage <= 0) && (s->delete_me)) evas_smart_free(s);

       }

 

Smart Data 的析构

  • 每个控件都会有_smart_del函数

–      通过该函数清理自己的ex data,最后调用父类的_smart_del函数

–      最后会调用到widget基类的smart_del函数

–      Widget清理完相关数据后最后会

 free(sd);

 evas_object_smart_data_set(obj, NULL);

 

调用父类的del函数

ELM_WIDGET_CLASS(xxxx_parent_sc)->base.del(obj);

如:

ELM_WIDGET_CLASS(_elm_layout_parent_sc)->base.del(obj);

 

什么是EO

  • 一个lib库名称
  • 一个方便快捷,帮助C的对象化编程工具

–      EO其实就是为了减轻程序猿工作量(重复而且boring)

–      C + EO能够模拟对象化语言,可以作为C ++ ,objective-c的替代方案

  • EO是EFL的对象化系统基础

 

Eo是对Gobject的翻版

 

Gobject介绍

  • GObject System以Gtype为基础而实现的一套单根继承的C语言的面向对象的框架。
  • GObject对象系统是一个建立在GLIB基础上的,用C语言完成的,具有跨平台特色的、灵活的、可扩展的、非常容易映射到其它语言的面向对象的框架。如果你是一个C语言的执着的追随者,你没有理由不研究一下它。-  IBM.com
  • GLib中最有特色的是它的对象系统--GObject System,它是以Gtype为基础而实现的一套单根继承的C语言的面向对象的框架。-IBM.com

 

  • Gobject模拟封装。

–      类是两个结构体的组合,一个是实例结构体,另一个是类结构体。

  • GType 是GLib 运行时类型认证和管理系统。

–      Gtype提供了注册和管理所有基本数据类型、用户定义对象和界面类型的技术实现

  • Gobject实现多态

–      为每个子类在内存中保存了一份包含成员函数指针的表(虚方法表vtable)

  • 等等(太多东西了,不说了)

 

EO-EFL的“GObject”

  • 为什么要重新发明轮子?

–      引入Gobject需要导入Glib, bla~ bla~ bla~

  • C具有良好的兼容性
  • EO会加入Tizen中

 

就目前的C++编译器来说,并没有标准的ABI可以在所有的C++编译器运行(除了Windows,Windows上有COM可以处理),以A这个C++编译器所编译出来的库,并不一定能被以B C++编译器所编译的程序调用。如果需要这样的兼容性,C++的方法必须要输出为C的函数,这样就无法享受C++带来的好处了。这主要是因为不同的C++编译器使用了不同的名称特殊处理(Name mangling)以确保输出符号的独一性。(这是必要的,举例来说,不同的类可能会有一样名称的成员函数、被覆载许多次的函数名称,或者出现在多个名字空间但同名的函数,但在输出为目标文件时,这些代码都是独立的,如果名称都一样,会被视为同一份代码,因此需要对名称作特殊处理。)对照C来看,C不支持任何形式的覆载或名称特殊处理,C库的作者永远只能使用明确的前置名以确保输出名称的全域独一性。

 

EO

  • 继承(多继承)
  • 接口
  • 多态(overriding)
  • Callbacks
  • Mixins,reference counting,Weak references等等

 

posted @ 2015-04-27 16:16  ThomasLiao  Views(471)  Comments(2)    收藏  举报