转载时请注明出处和作者
作者 :李先静

前段时间对 syncmanager进行重构,为了减少不必要的开销,我决定在需要时才加SyncSource插件,不需要时就卸载它们。在测试时发现第一次运行时正 常,第二次运行时创建DbPersistance对象时失败了。仔细看了下调试信息,里面告诉我说注册DbPersistance类型失败,因为已经 DbPersistance类型注册了。不太可能啊,对象注册类型时一般都会防止重复注册的,比如:

 

gobject对象不宜作为动态加载的插件_nullgtk_button_get_type ( void )
gobject对象不宜作为动态加载的插件_null
gobject对象不宜作为动态加载的插件_gtk_03gobject对象不宜作为动态加载的插件_button_04 ... {
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_gtk_05  static GType button_type = 0;
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_gtk_05 
gobject对象不宜作为动态加载的插件_gtk_05  if (!button_type)
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_null_11gobject对象不宜作为动态加载的插件_gtk_12    ...{
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_gtk_05      static const GTypeInfo button_info =
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_null_11gobject对象不宜作为动态加载的插件_gtk_12      ...{
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_gtk_05    sizeof (GtkButtonClass),
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_null_11gobject对象不宜作为动态加载的插件_gtk_12    NULL,       /**//* base_init */
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_null_11gobject对象不宜作为动态加载的插件_gtk_12    NULL,       /**//* base_finalize */
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_gtk_05    (GClassInitFunc) gtk_button_class_init,
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_null_11gobject对象不宜作为动态加载的插件_gtk_12    NULL,       /**//* class_finalize */
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_null_11gobject对象不宜作为动态加载的插件_gtk_12    NULL,       /**//* class_data */
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_gtk_05    sizeof (GtkButton),
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_null_11gobject对象不宜作为动态加载的插件_gtk_12    16,     /**//* n_preallocs */
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_gtk_05    (GInstanceInitFunc) gtk_button_init,
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_button_42      };
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_gtk_05 
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_gtk_05      button_type = g_type_register_static (GTK_TYPE_BIN, "GtkButton",
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_gtk_05                        &button_info, 0);
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_button_42    }
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_gtk_05 
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_gtk_05  return button_type;
gobject对象不宜作为动态加载的插件_gtk_05
gobject对象不宜作为动态加载的插件_class_56}
gobject对象不宜作为动态加载的插件_null
gobject对象不宜作为动态加载的插件_null

 

这里的button_type静态变量可以防止类型重复注册,大家都是按种方式处理的,难道DbPersistance的注册函数有什么特殊吗?看了一下代码,果然有点不同:

gobject对象不宜作为动态加载的插件_nullG_DEFINE_TYPE (DbPersistance, db_persistance, G_TYPE_OBJECT)

 

它用glib提供的宏实现的,再看看G_DEFINE_TYPE的定义,展开出来的代码也差不多。没有什么线索,只好用gdb跟到里面看一下。发现g_define_type_id这个静态变量第二次进去时还是0,回忆一下对代码所做的改动,一下明白了其中的原理。

这个插件是个动态库,第 一次运行时,先加载它,然后调用bpersistance_get_type, g_define_type_id确实被初始化了,用完之后它被卸载了。 第二次运行时,这个插件重新被加载,动态库中的bss段被清零,g_define_type_id自然被初始化为0了,而管理gobject的对象系统是 放在malloc分配出来的堆里面,它是一直存在的,只是里面的数据有些是无效的。结果调用dbpersistance_get_type时,就会出现重 复注册的问题。

gobject并没有提供注销类型的接口,即使有这样的接口也很麻烦,插件要知道自己何时被卸载,并注销插件中的全部类型(这个很难做到)。为了解决这个问题,我只好在编译时链接相关的动态库,在某种程度上说这失去了插件的意义,但也没有想到好的办法。

看来在这种情况下,最好是避免使用Gobject。