minigui/mgncs:使用哈希表(HashTable)实现窗口局部变量(Widget Local)机制
需求说明
在程序程序UI界面设计的时候,经常会遇到这样的情形,需要一个临时变量来保存一个值,这个值还会被窗口的其他消息响应函数用到,窗口销毁这个变量也就没有用了,也就是说这个变量只在窗口生命周期存在,类比线程局部变量(Thread Local)的概念,我们可以把它叫做窗口局部变量(Widget Local)。
然而不论是Windows上的MFC控件库,还是QT,还是现在我们项目中使用的MiniGUI,现行的所有GUI框架都没有为窗口对象提供动态定义变量的功能。
之前遇到这种需要,我只能用一个全局静态变量(static)来代替,但这种方式是不安全的,如果同一个窗口拥有两个以上实例的时候更是不能使用。如果大量无顾忌的使用,会为项目的稳定性埋下隐患。
最近UI层与业务层数据绑定功能的时候,再次遇到了这个问题,而且无论如何绕不过去了,只能下决心,自行设计了窗口局部变量(Widget Local)机制。
实现原理
其原理说道起来并不复杂,就是通过一个哈希表来保存每个窗口创建的任意多个局部变量(Widget Local),并侦听窗口的MSG_DESTROY消息,当窗口销毁时自动销毁所有局部变量。每个窗口的局部变量数据都保存一个独立的哈希表中。有了这个机制,就可以安全的在窗口中定义局部变量,而不用关心变量的销毁问题,还可以同时访问不同窗口的局部变量。
代码实现
哈希表
对WidetLocal变量的读写在代码实现这一层其实就是对哈希表的读写操作,那么C下面如何实现哈希表呢?
难道要自己写一个?
其实MiniGUI/mgncs1.2.0版本,将原本其内部使用的哈希表(hashtable.h)开放出来了,所以C下面如何实现哈希表不用操心了,直接使用mgncs自带的就好了。
下面是实现代码 fl_widget_local.c
/** fl_widget_local.c** Created on: Aug 13, 2018* Author: gyd*/#include "fl_widget_local.h"/** 保存所有窗口 WidgetLocal变量的哈希表,程序结束时自动销毁* key type mWidget** value type fl_owner_data_t** */
static HashTable * ht_widgets = NULL;#define MAX_WIDGET_COUNT 37
#define MAX_WL_VAR 7#ifdef _MGRM_THREADS
static pthread_mutex_t _wl_locker;
#define WL_LOCK() (pthread_mutex_lock(&_wl_locker))
#define WL_UNLOCK() (pthread_mutex_unlock(&_wl_locker))
#else
#define WL_LOCK()
#define WL_UNLOCK()
#endif// 窗口数据
typedef struct _fl_owner_data_t{/* 窗口(widget)原有的 MSG_DESTROY 消息处理函数 */NCS_CB_ONVOID ondestory_handler;/* 保存所有局部变量(widget lcoal)的哈希表* key type ht_key_t* value type fl_wlocal_t** */HashTable* vars;
}fl_owner_data_t;static void free_wl_entry_obj(fl_wlocal_t* obj)
{if(obj){// 如果有free函数则执行if(obj->free_value)obj->free_value((void*)obj->value);free(obj);}
}
static void free_owner_entry_obj(fl_owner_data_t* obj)
{if(obj){// 销毁(哈希表中的)所有变量hash_free(obj->vars);free(obj);}
}// 用于拦截窗口的 MSG_DESTROY 消息
static void ondestroy_listener(mWidget*self, UINT message)
{fl_owner_data_t* owner_data = (fl_owner_data_t*)hash_get(ht_widgets, (ht_key_t)self);assert(owner_data);// 先执行原有的 MSG_DESTROY 处理函数if(owner_data->ondestory_handler)owner_data->ondestory_handler(self,message);if(MSG_DESTROY == message)// 多一层判断增加安全性hash_delete(ht_widgets,(ht_key_t)self);/* 销毁mWidget对象所有的widget local变量 */
}
static void fl_widget_local_set(mWidget* owner,ht_key_t key,fl_wlocal_t *wlocal)
{// 检查输入参数,为0返回if(!owner || !key)return;void do_finally( ){WL_UNLOCK();return ;}WL_LOCK();// lazy initialize 哈希表延迟初始化if(ht_widgets == NULL){ht_widgets = hash_new(MAX_WIDGET_COUNT, (freeObj)free_owner_entry_obj);}fl_owner_data_t* owner_data = (fl_owner_data_t*)hash_get(ht_widgets, (ht_key_t)owner);if(!owner_data){// 创建 widget local hashTable,并向mWidget绑定 MSG_DESTROY 消息侦听器函数owner_data = (fl_owner_data_t*)calloc(1,sizeof(fl_owner_data_t));owner_data->vars = hash_new(MAX_WL_VAR, (freeObj)free_wl_entry_obj);// 替换控件的MSG_DESTROY消息处理函数owner_data->ondestory_handler = ncsSetComponentHandler((mComponent*)owner, MSG_DESTROY,ondestroy_listener);hash_insert(ht_widgets, (ht_key_t)owner, owner_data);}if(wlocal){fl_wlocal_t *var = (fl_wlocal_t *)hash_get(owner_data->vars,key);// 如果变量已经存在且相等则跳过赋值,避免hash_insert时执行freeObject// 注意:这里是判断value变量所有8个字节完全相等if(var && !memcmp(&var->value,&wlocal->value,sizeof(var->value)))return do_finally();var = (fl_wlocal_t *)calloc(1,sizeof(fl_wlocal_t));assert(var);*var = *wlocal;// 变量赋值hash_insert(owner_data->vars,key,var);}else{// 为NULL删除变量hash_delete(owner_data->vars,key);}return do_finally();
}static fl_wlocal_t* fl_widget_local_get(mWidget* owner,ht_key_t key)
{WL_LOCK();fl_wlocal_t * var = NULL;fl_owner_data_t* owner_data = (fl_owner_data_t*)hash_get(ht_widgets, (ht_key_t)owner);if(owner_data){var = (fl_wlocal_t*)hash_get(owner_data->vars,key);}WL_UNLOCK();return var;
}
#define FL_DERIVED_CLASS_CONSTRUCTOR_PRIORITY 201// 执行哈希表 ht_widget_local 实例初始化
void wl_instance_init() __attribute__((constructor(FL_DERIVED_CLASS_CONSTRUCTOR_PRIORITY + 1)));
// 释放ht_widget_local
void wl_instance_uninit() __attribute__((destructor(FL_DERIVED_CLASS_CONSTRUCTOR_PRIORITY + 1)));void wl_instance_init() {
}
void wl_instance_uninit(){hash_free(ht_widgets);
}
下面是头文件定义fl_widget_local.h
/** fl_widget_local.h** Created on: Aug 13, 2018* Author: gyd*/#ifndef MGCOMMON_FL_WIDGET_LOCAL_H_
#define MGCOMMON_FL_WIDGET_LOCAL_H_#include
#include
#include
#include
#include
#include
/*** key的类型为unsigned long,可以调用MiniGUI库函数 Str2Key 将字符串转为ht_key_t* */
// widget local局部变量类型定义
typedef struct _fl_wlocal_t {/* 变量值可为任意类型但长度不可超过 8 bytes */int64_t value;/* 变量释放函数指针,由定义变量时定义,用于销毁变量时执行释放变量占用的资源,可为NULL */freeObj free_value;
}fl_wlocal_t;/* 设置widget local变量* wlocal为NULL时删除变量* */
void fl_widget_local_set(mWidget* owner,ht_key_t key,fl_wlocal_t *wlocal);
/* 返回 widget local变量, 没找到则返回 NULL */
fl_wlocal_t* fl_widget_local_get(mWidget* owner,ht_key_t key);// 删除key指定的widget local
#define WLOCAL_DEL(owner,key) fl_widget_local_set((mWidget*)owner,(ht_key_t)key,NULL)// 设置key指定的widget local
#define WLOCAL_SET(owner,key,_value,cb_free) \
{\fl_wlocal_t wl = {.value=(int64_t)_value,.free_value=(freeObj)cb_free}; \fl_widget_local_set((mWidget*)owner,(ht_key_t)key,&wl);\
}\// 返回key指定的widget local的值的地址,未定义则返回NULL
// type 为 widget local的类型
#define WLOCAL_GET(owner,key,type) \
({\fl_wlocal_t* wl = fl_widget_local_get((mWidget*)owner,(ht_key_t)key);\(type*)(wl?&wl->value:NULL);\
})\// 定义一个不需要free的简单变量
#define WLOCAL_SET_SIMPLE(owner,key,value) WLOCAL_SET(owner,key,value,NULL)// 定义一个自动销毁的mObject 变量
#define WLOCAL_mObject(owner,value) WLOCAL_SET(owner,value,value,deleteObject)// 定义一个可以直接调用(free)销毁的内存指针变量
#define WLOCAL_allocptr(owner,allocptr) WLOCAL_SET(owner,allocptr,allocptr,free)#endif /* MGCOMMON_FL_WIDGET_LOCAL_H_ */
调用示例
mObject obj = NEW(myObjClass);
// 如下就将mObject对象定义为一个widget local变量,后续不需要去负责DELETE,
// self窗口销毁时会自动调用deleteObject函数销毁对象
WLOCAL_mObject(self,obj);
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
