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)
运行某个东西或者其他的,怎么办呢?先set好,之后就可以:
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来实现 要继承的基类必须作为子类的成员放在子类结构的第一位
因为我们要强制类型转换,呵呵呵 Widget:smart_class ![]() 譬如有个void指针data指向上面的数据结构 那么我们可以用强制类型转换:
![]() 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构造函数
在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 ![]() 3.是参数调用 Widget的初始化: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的属性, ![]() 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的博客巴士! |