找回密码
 立即注册
查看: 3426|回复: 0

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

[复制链接]
发表于 2015-4-29 22:13:48 | 显示全部楼层 |阅读模式
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)
  1. <font size="4">void evas_object_data_set(
  2.                                     Evas_Object *obj,  //
  3.                                     const char *key,    //
  4.                                     const void *data    //
  5.                                 )</font>
复制代码
2.获取这个对象
  1. <font size="4">void evas_object_data_get(
  2.                                  const Evas_Object *obj,
  3.                                  const char *key
  4.                                 )</font>
复制代码
  1. <font size="4">复制代码
  2. typedef struct appdata{
  3. Evas_Object* win;
  4. Evas_Object* layout;
  5. Evas_Object* conform;
  6. Evas_Object* nf;
  7. Evas_Object* base_ly;
  8. Evas_Object* genlist;
  9. Elm_Object_Item *nf_it;
  10. } appdata_s;</font>
复制代码
在某个文件某个函数中我只知道appdata_s的nf这个对象,但是我想通知genlist这个对象去

运行某个东西或者其他的,怎么办呢?先set好,之后就可以:
  1. <font size="4">Evas_Object *genlist = evas_object_data_get(cv_data->navi, "main_list");</font>
复制代码
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来实现
要继承的基类必须作为子类的成员放在子类结构的第一位
  1. <font size="4">typedef struct son
  2. {
  3.     father base;//基类
  4.     int version;
  5. }</font>
复制代码
为什么呢?

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

Widget:smart_class

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

那么我们可以用强制类型转换:
  1. <font size="4">Evas_Smart_Class *sc = (Evas_Smart_Class *)data; //能获取a,b,c,d
  2. Elm_Widget_Smart_Class *wsc = (Elm_Widget_Smart_Class *) data; //获取更多的,e,f,g,h,i,j,
  3. Eml_MyWidget_class *mwsc = (Eml_MyWidget_class *)data; //获取更多的 , k,l,m,n,o,p</font>
复制代码
  1. <font size="4">typedef struct _Elm_Genlist_Smart_Class
  2. {
  3.        Elm_Layout_Smart_Class base;//基类
  4.        int  version;   
  5. } Elm_Genlist_Smart_Class;</font>
复制代码

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构造函数
  1. xxxx_add()
复制代码
  调用
  1. obj = elm_widget_add( <b>_elm_layout_widget_smart_class_new() </b>, 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的属性,bj->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等等
本文来自于ThomasLiao的博客巴士!


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
欢迎来到泰泽网:http://www.tizennet.com/ 泰泽论坛:http://bbs.tizennet.com/ 好没有内涵哦,快到设置中更改这个无聊的签名吧!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|泰泽应用|泰泽论坛|泰泽网|小黑屋|Archiver|手机版|泰泽邮箱|泰泽网 ( 蜀ICP备13024062号-1 )

GMT+8, 2024-4-20 19:46 , Processed in 0.086865 second(s), 21 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表