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);

你可能感兴趣的:(embedded,minigui,MiniGUI)