前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >minigui/mgncs:使用哈希表(HashTable)实现窗口局部变量(Widget Local)机制

minigui/mgncs:使用哈希表(HashTable)实现窗口局部变量(Widget Local)机制

作者头像
10km
发布2019-05-25 22:20:47
4630
发布2019-05-25 22:20:47
举报
文章被收录于专栏:10km的专栏10km的专栏10km的专栏

版权声明:本文为博主原创文章,转载请注明源地址。 https://cloud.tencent.com/developer/article/1433665

需求说明

在程序程序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 <minigui/common.h>
#include <minigui/minigui.h>
#include <minigui/gdi.h>
#include <minigui/window.h>
#include <mgncs/mcommon.h>
#include <mgncs/mgncs.h>
/**
 * 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);
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年08月14日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 需求说明
  • 实现原理
  • 代码实现
    • 哈希表
    • 调用示例
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档