看看GtkWindow如何被生出来的(gtk_window_new()的内部机制)
来源:互联网 发布:大数据服务器搭建 编辑:程序博客网 时间:2024/04/29 18:36
http://blog.csdn.net/cuijpus/article/details/4245828
看看GtkWindow如何被生出来的.
用Glib/GObject/Gtk也有很长时间了,没有时间往下刨根,现在刨一刨。
1 定义GtkWindow
2 展开它:
在gtype.h中定义如下宏:
把定义的宏展开:
再把GtkBin的定义展开:
再展开GtkContainer的定义://不用展开,直接定义的
GtkWidget的get_type()也是直接定义的
GtkObject的get_type()也是直接定义的:
看看GObject的定义:gtk_init()后,这个对象是已经建立了
3 在用的时候,是直接调用一个宏:
下面我们来跟着一下其流程:(步步跟踪其父类,如果父类还没有创建,则先创建父类)
Step 1: 程序会调用这个接口:
Step 2: 接着会调用 (层层上访)
Step 3:所要先看GtkObject的创立过程:
GtkObject对象存在了,那就看它的子类吧:GtkWidget
接着往下搞:GtkWidget对象也有了,在往下看子类:GtkContainer
Go on: GtkBin
GtkBin创建后,GtkWindow对象也就创建了;
这样一系列的对象存在如下:
上面仅仅是一些对象,那这些对象携带的回调函数都是从哪里调用的呢?
关键之处是g_object_new()里面调用的函数
重点看看g_object_newv:
g_type_class_ref这个就是核心:递归调用
上面的递归调用会调用函数type_class_init_Wm()完成对某个类的初始化,即,分配内存同时调用class_init()
Base_class_init()调用的顺序:
1. g_object_base_class_init
2 gtk_object_base_class_init
3 gtk_widget_base_class_init 是NULL所以不调用
4 gtk_container_base_class_init
5 gtk_bin, gtk_windonw的base_class_init()都是NULL
下面开始class_init:
class_init() 之前有个递归过程(图示)
下面用运行代码来检查实际的过程:
条件:
结果:
1 定义GtkWindow
G_DEFINE_TYPE_WITH_CODE (GtkWindow, gtk_window, GTK_TYPE_BIN,
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
gtk_window_buildable_interface_init))
2 展开它:
在gtype.h中定义如下宏:
#define G_DEFINE_TYPE_WITH_CODE(TN, t_n, T_P, _C_) _G_DEFINE_TYPE_EXTENDED_BEGIN (TN, t_n, T_P, 0) {_C_;} _G_DEFINE_TYPE_EXTENDED_END()
#define _G_DEFINE_TYPE_EXTENDED_BEGIN(TypeName, type_name, TYPE_PARENT, flags) /
/
static void type_name##_init (TypeName *self); /
static void type_name##_class_init (TypeName##Class *klass); /
static gpointer type_name##_parent_class = NULL; /
static void type_name##_class_intern_init (gpointer klass) /
{ /
type_name##_parent_class = g_type_class_peek_parent (klass); /
type_name##_class_init ((TypeName##Class*) klass); /
} /
/
GType /
type_name##_get_type (void) /
{ /
static volatile gsize g_define_type_id__volatile = 0; /
if (g_once_init_enter (&g_define_type_id__volatile)) /
{ /
GType g_define_type_id = /
g_type_register_static_simple (TYPE_PARENT, /
g_intern_static_string (#TypeName), /
sizeof (TypeName##Class), /
(GClassInitFunc) type_name##_class_intern_init, /
sizeof (TypeName), /
(GInstanceInitFunc) type_name##_init, /
(GTypeFlags) flags); /
{ /* custom code follows */
#define _G_DEFINE_TYPE_EXTENDED_END() /
/* following custom code */ /
} /
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); /
} /
return g_define_type_id__volatile; /
} /* closes type_name##_get_type() */
把定义的宏展开:
static void gtk_window_init (GtkWindow *self);
static void gtk_window_class_init (GtkWindowClass * klass);
static void gtk_window_parent_class = NULL;
static void gtk_window_class_intern_init (gpointer klass)
{
gtk_window_parent_class = g_type_class_peek_parent (klass);
gtk_window_class_init ((GtkWindowClass*) klass);
}
GType
gtk_window_get_type (void)
{
static volatile gsize g_define_type_id__volatile = 0;
if (g_once_init_enter (&g_define_type_id__volatile))
{
GType g_define_type_id =
g_type_register_static_simple (GTK_TYPE_BIN, //父亲
g_intern_static_string ("GtkWindow"),
sizeof (GtkWindowClass),
(GClassInitFunc) gtk_window_class_intern_init,
sizeof (GtkWindow),
(GInstanceInitFunc) gtk_window_init,
0);
{
const GInterfaceInfo g_implement_interface = {
(GInterfaceInitFunc) gtk_window_buildable_interface_init, NULL, NULL };
g_type_add_interface_static (g_define_type_id, GTK_TYPE_BUILDABLE, &g_implement_interface);
}
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
}
return g_define_type_id__volatile;
}
再把GtkBin的定义展开:
G_DEFINE_ABSTRACT_TYPE (GtkBin, gtk_bin, GTK_TYPE_CONTAINER)
static void gtk_bin_init (GtkBin *self);
static void gtk_bin_class_init (GtkBinClass * klass);
static void gtk_bin_parent_class = NULL;
static void gtk_bin_class_intern_init (gpointer klass)
{
gtk_bin_parent_class = g_type_class_peek_parent (klass);
gtk_bin_class_init ((GtkBinClass*) klass);
}
GType
gtk_bin_get_type(void)
{
static volatile gsize g_define_type_id__volatile = 0;
if (g_once_init_enter (&g_define_type_id__volatile))
{
GType g_define_type_id =
g_type_register_static_simple (GTK_TYPE_CONTAINER, //GtkBin的父亲
g_intern_static_string ("GtkBin"),
sizeof (GtkBinClass),
(GClassInitFunc) gtk_bin_class_intern_init,
sizeof (GtkBin),
(GInstanceInitFunc) gtk_bin_init,
G_TYPE_FLAG_ABSTRACT);
{
}
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
}
return g_define_type_id__volatile;
}
再展开GtkContainer的定义://不用展开,直接定义的
#define GTK_TYPE_CONTAINER (gtk_container_get_type ())
/* --- functions --- */
GType
gtk_container_get_type (void)
{
static GType container_type = 0;
if (!container_type)
{
const GTypeInfo container_info =
{
sizeof (GtkContainerClass),
(GBaseInitFunc) gtk_container_base_class_init,
(GBaseFinalizeFunc) gtk_container_base_class_finalize,
(GClassInitFunc) gtk_container_class_init,
NULL /* class_finalize */,
NULL /* class_data */,
sizeof (GtkContainer),
0 /* n_preallocs */,
(GInstanceInitFunc) gtk_container_init,
NULL, /* value_table */
};
static const GInterfaceInfo buildable_info =
{
(GInterfaceInitFunc) gtk_container_buildable_init,
NULL,
NULL
};
container_type =
g_type_register_static (GTK_TYPE_WIDGET, //GtkContainer的父亲
I_("GtkContainer"),
&container_info, G_TYPE_FLAG_ABSTRACT);
g_type_add_interface_static (container_type,
GTK_TYPE_BUILDABLE,
&buildable_info);
}
return container_type;
}
GtkWidget的get_type()也是直接定义的
/* --- functions --- */
GType
gtk_widget_get_type (void)
{
static GType widget_type = 0;
if (G_UNLIKELY (widget_type == 0))
{
const GTypeInfo widget_info =
{
sizeof (GtkWidgetClass),
NULL, /* base_init */
(GBaseFinalizeFunc) gtk_widget_base_class_finalize,
(GClassInitFunc) gtk_widget_class_init,
NULL, /* class_finalize */
NULL, /* class_init */
sizeof (GtkWidget),
0, /* n_preallocs */
(GInstanceInitFunc) gtk_widget_init,
NULL, /* value_table */
};
const GInterfaceInfo accessibility_info =
{
(GInterfaceInitFunc) gtk_widget_accessible_interface_init,
(GInterfaceFinalizeFunc) NULL,
NULL /* interface data */
};
const GInterfaceInfo buildable_info =
{
(GInterfaceInitFunc) gtk_widget_buildable_interface_init,
(GInterfaceFinalizeFunc) NULL,
NULL /* interface data */
};
widget_type = g_type_register_static (GTK_TYPE_OBJECT, //GtkWidget的父亲
"GtkWidget",
&widget_info, G_TYPE_FLAG_ABSTRACT);
g_type_add_interface_static (widget_type, ATK_TYPE_IMPLEMENTOR,
&accessibility_info) ;
g_type_add_interface_static (widget_type, GTK_TYPE_BUILDABLE,
&buildable_info) ;
}
return widget_type;
}
GtkObject的get_type()也是直接定义的:
#define GTK_TYPE_OBJECT (gtk_object_get_type ())
GType
gtk_object_get_type (void)
{
static GType object_type = 0;
if (!object_type)
{
const GTypeInfo object_info =
{
sizeof (GtkObjectClass),
(GBaseInitFunc) gtk_object_base_class_init,
(GBaseFinalizeFunc) gtk_object_base_class_finalize,
(GClassInitFunc) gtk_object_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GtkObject),
16, /* n_preallocs */
(GInstanceInitFunc) gtk_object_init,
NULL, /* value_table */
};
object_type = g_type_register_static
(G_TYPE_INITIALLY_UNOWNED, //GtkObject的父亲,就是GObject
I_("GtkObject"),
&object_info, G_TYPE_FLAG_ABSTRACT);
}
return object_type;
}
看看GObject的定义:gtk_init()后,这个对象是已经建立了
GType
g_object_get_type (void)
{
return G_TYPE_OBJECT;
}
//GObject的回调函数什么时候被调用的?
3 在用的时候,是直接调用一个宏:
#define GTK_TYPE_WINDOW (gtk_window_get_type ())
下面我们来跟着一下其流程:(步步跟踪其父类,如果父类还没有创建,则先创建父类)
Step 1: 程序会调用这个接口:
GtkWidget* gtk_window_new (GtkWindowType type);
这个函数内部会调用
window = g_object_new (GTK_TYPE_WINDOW, NULL);
Step 2: 接着会调用 (层层上访)
GType
gtk_window_get_type (void)
//这个函数内部又会找GtkWindows的父类:GtkBin
GtkBin又找其父类GtkContainer,
GtkContainer又找其父类GtkWidget
GtkWidget又找其父类GtkObject
GtkObject又找其父类Gobject, 这个类在gtk_init()后,就已经存在了;
所以在反过来进行
Step 3:所要先看GtkObject的创立过程:
GType
gtk_object_get_type (void)
{
static GType object_type = 0;
if (!object_type)
{
const GTypeInfo object_info =
{
sizeof (GtkObjectClass),
(GBaseInitFunc) gtk_object_base_class_init,
(GBaseFinalizeFunc) gtk_object_base_class_finalize,
(GClassInitFunc) gtk_object_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GtkObject),
16, /* n_preallocs */
(GInstanceInitFunc) gtk_object_init,
NULL, /* value_table */
};
//g_type_register_static就从GObject派生出来一个类GtkObject,并填充好以后,放到全局的数组中,以及Hash Table中,具体如下:
//这个函数首先干的事情:检查名字是否合法以及在当前的Hash Table中是不是已经有了?然后再检查和指定的父类之间是否真的具有继承关系
//如果可以注册,
//如果父类不存在,直接放到全局数组中,则先在static_fundamental_type_nodes[]数组中找个位置,来存放这个类型节点指针
//如果父类存在,则从父类拷贝一份内存出来,作为子类, 主要在type_node_any_new_W()中完成,并且把这个新的子类放到父类的一个子类数组中
// 然后把新类放到Hash Table中,这样方便查询
// 然后给类型数据填充一些字段,主要是注册一些回调函数,由type_data_make_W()来完成
//同时把ref_count置为1,表明这个对象活了!
object_type = g_type_register_static (G_TYPE_INITIALLY_UNOWNED, I_("GtkObject"),
&object_info, G_TYPE_FLAG_ABSTRACT);
}
return object_type;
}
//还有问题没有搞明白,注册的系列回调,何时调用的?
GtkObject对象存在了,那就看它的子类吧:GtkWidget
/* --- functions --- */
GType
gtk_widget_get_type (void)
{
static GType widget_type = 0;
if (G_UNLIKELY (widget_type == 0))
{
const GTypeInfo widget_info =
{
sizeof (GtkWidgetClass),
NULL, /* base_init */
(GBaseFinalizeFunc) gtk_widget_base_class_finalize,
(GClassInitFunc) gtk_widget_class_init,
NULL, /* class_finalize */
NULL, /* class_init */
sizeof (GtkWidget),
0, /* n_preallocs */
(GInstanceInitFunc) gtk_widget_init,
NULL, /* value_table */
};
const GInterfaceInfo accessibility_info =
{
(GInterfaceInitFunc) gtk_widget_accessible_interface_init,
(GInterfaceFinalizeFunc) NULL,
NULL /* interface data */
};
const GInterfaceInfo buildable_info =
{
(GInterfaceInitFunc) gtk_widget_buildable_interface_init,
(GInterfaceFinalizeFunc) NULL,
NULL /* interface data */
};
//也是从GtkObject对象做一个内存拷贝,然后填充数据(注册回调),新创建的对象GtkWidget地址存放于GtkObject维护的一个子类列表里面;当然了,在Hash Table里面也是有的;
widget_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkWidget",
&widget_info, G_TYPE_FLAG_ABSTRACT);
g_type_add_interface_static (widget_type, ATK_TYPE_IMPLEMENTOR,
&accessibility_info) ;
g_type_add_interface_static (widget_type, GTK_TYPE_BUILDABLE,
&buildable_info) ;
}
return widget_type;
}
接着往下搞:GtkWidget对象也有了,在往下看子类:GtkContainer
/* --- functions --- */
GType
gtk_container_get_type (void)
{
static GType container_type = 0;
if (!container_type)
{
const GTypeInfo container_info =
{
sizeof (GtkContainerClass),
(GBaseInitFunc) gtk_container_base_class_init,
(GBaseFinalizeFunc) gtk_container_base_class_finalize,
(GClassInitFunc) gtk_container_class_init,
NULL /* class_finalize */,
NULL /* class_data */,
sizeof (GtkContainer),
0 /* n_preallocs */,
(GInstanceInitFunc) gtk_container_init,
NULL, /* value_table */
};
static const GInterfaceInfo buildable_info =
{
(GInterfaceInitFunc) gtk_container_buildable_init,
NULL,
NULL
};
//也是从GtkWidget对象做一个内存拷贝,然后填充数据(注册回调),新创建的对象GtkContainer地址存放于GtkWidget维护的一个子类列表里面;当然了,在Hash Table里面也是有的;
container_type =
g_type_register_static (GTK_TYPE_WIDGET, I_("GtkContainer"),
&container_info, G_TYPE_FLAG_ABSTRACT);
g_type_add_interface_static (container_type,
GTK_TYPE_BUILDABLE,
&buildable_info);
}
return container_type;
}
Go on: GtkBin
static void gtk_bin_init (GtkBin *self);
static void gtk_bin_class_init (GtkBinClass * klass);
static void gtk_bin_parent_class = NULL;
static void gtk_bin_class_intern_init (gpointer klass)
{
gtk_bin_parent_class = g_type_class_peek_parent (klass);
gtk_bin_class_init ((GtkBinClass*) klass);
}
GType
gtk_bin_get_type(void)
{
static volatile gsize g_define_type_id__volatile = 0;
if (g_once_init_enter (&g_define_type_id__volatile))
{
GType g_define_type_id =
g_type_register_static_simple (GTK_TYPE_CONTAINER,
g_intern_static_string ("GtkBin"),
sizeof (GtkBinClass),
(GClassInitFunc) gtk_bin_class_intern_init,
sizeof (GtkBin),
(GInstanceInitFunc) gtk_bin_init,
G_TYPE_FLAG_ABSTRACT);
{
}
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
}
return g_define_type_id__volatile;
}
GtkBin创建后,GtkWindow对象也就创建了;
这样一系列的对象存在如下:
| `GtkObject
| |
| `GtkWidget
| |
| `GtkContainer
| |
| +GtkBin
| | |
| | +GtkWindow
对象的出生过程:老子先出生,然后是儿子,然后是孙子…; 自然界的规律
不过这里是先提出要孙子,谁提呢?如来佛吧!
然后如来就查查这个人的父亲是谁?已经出生了吗?没有再往上找;…
这些对象都有些本领(回调函数),一般情况下,也是先把老子的钱榨干,然后用儿子的,然后用…
上面仅仅是一些对象,那这些对象携带的回调函数都是从哪里调用的呢?
关键之处是g_object_new()里面调用的函数
要仔细的分析!
window = g_object_new (GTK_TYPE_WINDOW, NULL);
重点看看g_object_newv:
gpointer
g_object_newv (GType object_type,
guint n_parameters,
GParameter *parameters)
{
GObjectConstructParam *cparams, *oparams;
GObjectNotifyQueue *nqueue = NULL; /* shouldn't be initialized, just to silence compiler */
GObject *object;
GObjectClass *class, *unref_class = NULL;
GSList *slist;
guint n_total_cparams = 0, n_cparams = 0, n_oparams = 0, n_cvalues;
GValue *cvalues;
GList *clist = NULL;
gboolean newly_constructed;
guint i;
g_return_val_if_fail (G_TYPE_IS_OBJECT (object_type), NULL);
class = g_type_class_peek_static (object_type);
if (!class)
class = unref_class = g_type_class_ref (object_type);
for (slist = class->construct_properties; slist; slist = slist->next)
{
clist = g_list_prepend (clist, slist->data);
n_total_cparams += 1;
}
/* collect parameters, sort into construction and normal ones */
oparams = g_new (GObjectConstructParam, n_parameters);
cparams = g_new (GObjectConstructParam, n_total_cparams);
for (i = 0; i < n_parameters; i++)
{
GValue *value = ¶meters[i].value;
GParamSpec *pspec = g_param_spec_pool_lookup (pspec_pool,
parameters[i].name,
object_type,
TRUE);
if (!pspec)
{
g_warning ("%s: object class `%s' has no property named `%s'",
G_STRFUNC,
g_type_name (object_type),
parameters[i].name);
continue;
}
if (!(pspec->flags & G_PARAM_WRITABLE))
{
g_warning ("%s: property `%s' of object class `%s' is not writable",
G_STRFUNC,
pspec->name,
g_type_name (object_type));
continue;
}
if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
{
GList *list = g_list_find (clist, pspec);
if (!list)
{
g_warning ("%s: construct property /"%s/" for object `%s' can't be set twice",
G_STRFUNC, pspec->name, g_type_name (object_type));
continue;
}
cparams[n_cparams].pspec = pspec;
cparams[n_cparams].value = value;
n_cparams++;
if (!list->prev)
clist = list->next;
else
list->prev->next = list->next;
if (list->next)
list->next->prev = list->prev;
g_list_free_1 (list);
}
else
{
oparams[n_oparams].pspec = pspec;
oparams[n_oparams].value = value;
n_oparams++;
}
}
/* set remaining construction properties to default values */
n_cvalues = n_total_cparams - n_cparams;
cvalues = g_new (GValue, n_cvalues);
while (clist)
{
GList *tmp = clist->next;
GParamSpec *pspec = clist->data;
GValue *value = cvalues + n_total_cparams - n_cparams - 1;
value->g_type = 0;
g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
g_param_value_set_default (pspec, value);
cparams[n_cparams].pspec = pspec;
cparams[n_cparams].value = value;
n_cparams++;
g_list_free_1 (clist);
clist = tmp;
}
/* construct object from construction parameters */
object = class->constructor (object_type, n_total_cparams, cparams);
/* free construction values */
g_free (cparams);
while (n_cvalues--)
g_value_unset (cvalues + n_cvalues);
g_free (cvalues);
/* adjust freeze_count according to g_object_init() and remaining properties */
G_LOCK (construction_mutex);
newly_constructed = slist_maybe_remove (&construction_objects, object);
G_UNLOCK (construction_mutex);
if (newly_constructed || n_oparams)
nqueue = g_object_notify_queue_freeze (object, &property_notify_context);
if (newly_constructed)
g_object_notify_queue_thaw (object, nqueue);
/* run 'constructed' handler if there is one */
if (newly_constructed && class->constructed)
class->constructed (object);
/* set remaining properties */
for (i = 0; i < n_oparams; i++)
object_set_property (object, oparams[i].pspec, oparams[i].value, nqueue);
g_free (oparams);
/* release our own freeze count and handle notifications */
if (newly_constructed || n_oparams)
g_object_notify_queue_thaw (object, nqueue);
if (unref_class)
g_type_class_unref (unref_class);
return object;
}
g_type_class_ref这个就是核心:递归调用
gpointer
g_type_class_ref (GType type)
{
TypeNode *node;
GType ptype;
/* optimize for common code path */
G_WRITE_LOCK (&type_rw_lock);
//如果当前类型对应的节点数据已经存在了,并且也别初始化过,则紧紧增加一个ref_count,然后返回。
node = lookup_type_node_I (type);
if (node && node->is_classed && node->data &&
node->data->class.class &&
node->data->class.init_state == INITIALIZED)
{
type_data_ref_Wm (node);
G_WRITE_UNLOCK (&type_rw_lock);
return node->data->class.class;
}
if (!node || !node->is_classed ||
(node->data && node->data->common.ref_count < 1))
{
G_WRITE_UNLOCK (&type_rw_lock);
g_warning ("cannot retrieve class for invalid (unclassed) type `%s'",
type_descriptive_name_I (type));
return NULL;
}
//给当前节点分配内存等
type_data_ref_Wm (node);
//找到其父节点
ptype = NODE_PARENT_TYPE (node);
G_WRITE_UNLOCK (&type_rw_lock);
g_static_rec_mutex_lock (&class_init_rec_mutex); /* required locking order: 1) class_init_rec_mutex, 2) type_rw_lock */
/* here, we either have node->data->class.class == NULL, or a recursive
* call to g_type_class_ref() with a partly initialized class, or
* node->data->class.init_state == INITIALIZED, because any
* concurrently running initialization was guarded by class_init_rec_mutex.
*/
if (!node->data->class.class) /* class uninitialized */
{
/* acquire reference on parent class */
//这个地方是最重要的,会一直找到最上面的基类
GtkWindow-> GtkBin -> GtkContainer ->GtkWidget->GtkObject
GTypeClass *pclass = ptype ? g_type_class_ref (ptype) : NULL;
G_WRITE_LOCK (&type_rw_lock);
if (node->data->class.class) /* class was initialized during parent class initialization? */
INVALID_RECURSION ("g_type_plugin_*", node->plugin, NODE_NAME (node));
//当上面的递归调用入口的边界条件(ptype == NULL, 即发现GObject没有父类后,就会,就不会再次调用g_type_class_ref了,这时候函数调用堆栈就会返回就从最后一个g_type_class_ref开始往下执行了,此时是GObject, 所以会从类的顶层开始调用其Class_init(); 这样就实现了从父类,到子类,再到孙子类的class_init()的过程。比较巧妙。
type_class_init_Wm (node, pclass);
G_WRITE_UNLOCK (&type_rw_lock);
}
g_static_rec_mutex_unlock (&class_init_rec_mutex);
return node->data->class.class;
}
上面的递归调用会调用函数type_class_init_Wm()完成对某个类的初始化,即,分配内存同时调用class_init()
static void
type_class_init_Wm (TypeNode *node,
GTypeClass *pclass)
{
GSList *slist, *init_slist = NULL;
GTypeClass *class;
IFaceEntry *entry;
TypeNode *bnode, *pnode;
guint i;
g_assert (node->is_classed && node->data &&
node->data->class.class_size &&
!node->data->class.class &&
node->data->class.init_state == UNINITIALIZED);
class = g_malloc0 (node->data->class.class_size);
node->data->class.class = class;
node->data->class.init_state = BASE_CLASS_INIT;
if (pclass)
{
//找到GObjectClass对应的节点
TypeNode *pnode = lookup_type_node_I (pclass->g_type);
//拷贝给子类GtkWidgetClass, 这就是继承?
memcpy (class, pclass, pnode->data->class.class_size);
if (node->is_instantiatable)
{
/* We need to initialize the private_size here rather than in
* type_data_make_W() since the class init for the parent
* class may have changed pnode->data->instance.private_size.
*/
node->data->instance.private_size = pnode->data->instance.private_size;
}
}
class->g_type = NODE_TYPE (node);
G_WRITE_UNLOCK (&type_rw_lock);
/* stack all base class initialization functions, so we
* call them in ascending order.
*/
//这个遍历是从GtkWindow-> GtkBin -> GtkContainer ->GtkWidget->GtkObject->GObject ,即从子到父亲的顺序;
//而列举出的base_class_init()函数的列表正好是反的:
GObject::class_init_base() à GtkObject::class_init_base()à GtkWidget::class_init_base()àGtkContainer::class_init_base()à GtkBin::class_init_base()à GtkWidow::class_init_base()
//所以首先会调用基类的base_class_init()函数,然后依次进行下去
for (bnode = node; bnode; bnode = lookup_type_node_I (NODE_PARENT_TYPE (bnode)))
if (bnode->data->class.class_init_base)
init_slist = g_slist_prepend (init_slist, (gpointer) bnode->data->class.class_init_base);
for (slist = init_slist; slist; slist = slist->next)
{
GBaseInitFunc class_init_base = (GBaseInitFunc) slist->data;
class_init_base (class);
}
g_slist_free (init_slist);
G_WRITE_LOCK (&type_rw_lock);
node->data->class.init_state = BASE_IFACE_INIT;
/* Before we initialize the class, base initialize all interfaces, either
* from parent, or through our holder info
*/
pnode = lookup_type_node_I (NODE_PARENT_TYPE (node));
i = 0;
while (i < CLASSED_NODE_N_IFACES (node))
{
entry = &CLASSED_NODE_IFACES_ENTRIES (node)[i];
while (i < CLASSED_NODE_N_IFACES (node) &&
entry->init_state == IFACE_INIT)
{
entry++;
i++;
}
if (i == CLASSED_NODE_N_IFACES (node))
break;
if (!type_iface_vtable_base_init_Wm (lookup_type_node_I (entry->iface_type), node))
{
guint j;
/* need to get this interface from parent, type_iface_vtable_base_init_Wm()
* doesn't modify write lock upon FALSE, so entry is still valid;
*/
g_assert (pnode != NULL);
for (j = 0; j < CLASSED_NODE_N_IFACES (pnode); j++)
{
IFaceEntry *pentry = CLASSED_NODE_IFACES_ENTRIES (pnode) + j;
if (pentry->iface_type == entry->iface_type)
{
entry->vtable = pentry->vtable;
entry->init_state = INITIALIZED;
break;
}
}
g_assert (entry->vtable != NULL);
}
/* If the write lock was released, additional interface entries might
* have been inserted into CLASSED_NODE_IFACES_ENTRIES (node); they'll
* be base-initialized when inserted, so we don't have to worry that
* we might miss them. Uninitialized entries can only be moved higher
* when new ones are inserted.
*/
i++;
}
node->data->class.init_state = CLASS_INIT;
G_WRITE_UNLOCK (&type_rw_lock);
//这句话非常重要,开始class_init的过程了!!!
//从gtk_window_class_init()开始?
if (node->data->class.class_init)
node->data->class.class_init (class, (gpointer) node->data->class.class_data);
G_WRITE_LOCK (&type_rw_lock);
node->data->class.init_state = IFACE_INIT;
/* finish initializing the interfaces through our holder info.
* inherited interfaces are already init_state == INITIALIZED, because
* they either got setup in the above base_init loop, or during
* class_init from within type_add_interface_Wm() for this or
* an anchestor type.
*/
i = 0;
while (TRUE)
{
entry = &CLASSED_NODE_IFACES_ENTRIES (node)[i];
while (i < CLASSED_NODE_N_IFACES (node) &&
entry->init_state == INITIALIZED)
{
entry++;
i++;
}
if (i == CLASSED_NODE_N_IFACES (node))
break;
type_iface_vtable_iface_init_Wm (lookup_type_node_I (entry->iface_type), node);
/* As in the loop above, additional initialized entries might be inserted
* if the write lock is released, but that's harmless because the entries
* we need to initialize only move higher in the list.
*/
i++;
}
node->data->class.init_state = INITIALIZED;
}
Base_class_init()调用的顺序:
1. g_object_base_class_init
static void
g_object_base_class_init (GObjectClass *class)
{
GObjectClass *pclass = g_type_class_peek_parent (class);
/* reset instance specific fields and methods that don't get inherited */
class->construct_properties = pclass ? g_slist_copy (pclass->construct_properties) : NULL;
class->get_property = NULL;
class->set_property = NULL;
}
2 gtk_object_base_class_init
static void
gtk_object_base_class_init (GtkObjectClass *class)
{
/* reset instance specifc methods that don't get inherited */
class->get_arg = NULL;
class->set_arg = NULL;
}
3 gtk_widget_base_class_init 是NULL所以不调用
4 gtk_container_base_class_init
static void
gtk_container_base_class_init (GtkContainerClass *class)
{
/* reset instance specifc class fields that don't get inherited */
class->set_child_property = NULL;
class->get_child_property = NULL;
}
5 gtk_bin, gtk_windonw的base_class_init()都是NULL
下面开始class_init:
class_init() 之前有个递归过程(图示)
GtkWindow
g_type_class_ref
GtkBin
g_type_class_ref
GtkContainer
g_type_class_ref
GtkWidget
g_type_class_ref
GtkObject
g_type_class_ref
GObject
g_type_class_ref
class_init
class_init
class_init
class_init
class_init
class_init
注意:如果某个类在递归过程中检查是已经被初始化过的类,则跳过其class_init()函数。
下面用运行代码来检查实际的过程:
条件:
g_type_init();
GtkWidget *window =gtk_window_new(GTK_WINDOW_TOPLEVEL);
结果:
GObject: g_type_name (object_type) : GtkWindow
GObject: class == NULL.
GType: enter g_type_class_ref() times = 1.
GType: current type: GtkWindow
GType: type_data_ref_Wm(GtkWindow).
GType: find current type's GtkWindow parent: GtkBin.
GType: enter g_type_class_ref() times = 2.
GType: current type: GtkBin
GType: type_data_ref_Wm(GtkBin).
GType: find current type's GtkBin parent: GtkContainer.
GType: enter g_type_class_ref() times = 3.
GType: current type: GtkContainer
GType: type_data_ref_Wm(GtkContainer).
GType: find current type's GtkContainer parent: GtkWidget.
GType: enter g_type_class_ref() times = 4.
GType: current type: GtkWidget
GType: type_data_ref_Wm(GtkWidget).
GType: find current type's GtkWidget parent: GtkObject.
GType: enter g_type_class_ref() times = 5.
GType: current type: GtkObject
GType: type_data_ref_Wm(GtkObject).
GType: find current type's GtkObject parent: GInitiallyUnowned.
GType: enter g_type_class_ref() times = 6.
GType: current type: GInitiallyUnowned
GType: type_data_ref_Wm(GInitiallyUnowned).
GType: find current type's GInitiallyUnowned parent: GObject.
GType: enter g_type_class_ref() times = 7.
GType: current type: GObject
GType: type_data_ref_Wm(GObject).
GType: find current type's GObject parent: (null).
GType: Node name: GObject.
GType: Node name: GInitiallyUnowned.
GType: Node name: GtkObject.
GType: enter g_type_class_ref() times = 8.
GType: current type: GObject
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 9.
GType: current type: GParamPointer
GType: type_data_ref_Wm(GParamPointer).
GType: find current type's GParamPointer parent: GParam.
GType: enter g_type_class_ref() times = 10.
GType: current type: GParam
GType: type_data_ref_Wm(GParam).
GType: find current type's GParam parent: (null).
GType: Node name: GParam.
GType: Node name: GParamPointer.
GType: Node name: GtkWidget.
GType: enter g_type_class_ref() times = 11.
GType: current type: GParamString
GType: type_data_ref_Wm(GParamString).
GType: find current type's GParamString parent: GParam.
GType: enter g_type_class_ref() times = 12.
GType: current type: GParam
GType: current type already exist. return.
GType: Node name: GParamString.
GType: enter g_type_class_ref() times = 13.
GType: current type: GParamObject
GType: type_data_ref_Wm(GParamObject).
GType: find current type's GParamObject parent: GParam.
GType: enter g_type_class_ref() times = 14.
GType: current type: GParam
GType: current type already exist. return.
GType: Node name: GParamObject.
GType: enter g_type_class_ref() times = 15.
GType: current type: GParamInt
GType: type_data_ref_Wm(GParamInt).
GType: find current type's GParamInt parent: GParam.
GType: enter g_type_class_ref() times = 16.
GType: current type: GParam
GType: current type already exist. return.
GType: Node name: GParamInt.
GType: enter g_type_class_ref() times = 17.
GType: current type: GParamInt
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 18.
GType: current type: GParamBoolean
GType: type_data_ref_Wm(GParamBoolean).
GType: find current type's GParamBoolean parent: GParam.
GType: enter g_type_class_ref() times = 19.
GType: current type: GParam
GType: current type already exist. return.
GType: Node name: GParamBoolean.
GType: enter g_type_class_ref() times = 20.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 21.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 22.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 23.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 24.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 25.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 26.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 27.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 28.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 29.
GType: current type: GParamObject
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 30.
GType: current type: GdkEventMask
GType: type_data_ref_Wm(GdkEventMask).
GType: find current type's GdkEventMask parent: GFlags.
GType: enter g_type_class_ref() times = 31.
GType: current type: GFlags
GType: type_data_ref_Wm(GFlags).
GType: find current type's GFlags parent: (null).
GType: Node name: GFlags.
GType: Node name: GdkEventMask.
GType: enter g_type_class_ref() times = 32.
GType: current type: GParamFlags
GType: type_data_ref_Wm(GParamFlags).
GType: find current type's GParamFlags parent: GParam.
GType: enter g_type_class_ref() times = 33.
GType: current type: GParam
GType: current type already exist. return.
GType: Node name: GParamFlags.
GType: enter g_type_class_ref() times = 34.
GType: current type: GdkExtensionMode
GType: type_data_ref_Wm(GdkExtensionMode).
GType: find current type's GdkExtensionMode parent: GEnum.
GType: enter g_type_class_ref() times = 35.
GType: current type: GEnum
GType: type_data_ref_Wm(GEnum).
GType: find current type's GEnum parent: (null).
GType: Node name: GEnum.
GType: Node name: GdkExtensionMode.
GType: enter g_type_class_ref() times = 36.
GType: current type: GParamEnum
GType: type_data_ref_Wm(GParamEnum).
GType: find current type's GParamEnum parent: GParam.
GType: enter g_type_class_ref() times = 37.
GType: current type: GParam
GType: current type already exist. return.
GType: Node name: GParamEnum.
GType: enter g_type_class_ref() times = 38.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 39.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 40.
GType: current type: GParamString
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 41.
GType: current type: GParamString
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 42.
GType: current type: GParamObject
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 43.
GType: current type: GtkTouchSoundType
GType: type_data_ref_Wm(GtkTouchSoundType).
GType: find current type's GtkTouchSoundType parent: GEnum.
GType: enter g_type_class_ref() times = 44.
GType: current type: GEnum
GType: current type already exist. return.
GType: Node name: GtkTouchSoundType.
GType: enter g_type_class_ref() times = 45.
GType: current type: GParamEnum
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 46.
GType: current type: GtkSvibeEffectType
GType: type_data_ref_Wm(GtkSvibeEffectType).
GType: find current type's GtkSvibeEffectType parent: GEnum.
GType: enter g_type_class_ref() times = 47.
GType: current type: GEnum
GType: current type already exist. return.
GType: Node name: GtkSvibeEffectType.
GType: enter g_type_class_ref() times = 48.
GType: current type: GParamEnum
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 49.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 50.
GType: current type: GParamInt
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 51.
GType: current type: GParamString
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 52.
GType: current type: GParamInt
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 53.
GType: current type: GParamBoxed
GType: type_data_ref_Wm(GParamBoxed).
GType: find current type's GParamBoxed parent: GParam.
GType: enter g_type_class_ref() times = 54.
GType: current type: GParam
GType: current type already exist. return.
GType: Node name: GParamBoxed.
GType: enter g_type_class_ref() times = 55.
GType: current type: GParamBoxed
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 56.
GType: current type: GParamFloat
GType: type_data_ref_Wm(GParamFloat).
GType: find current type's GParamFloat parent: GParam.
GType: enter g_type_class_ref() times = 57.
GType: current type: GParam
GType: current type already exist. return.
GType: Node name: GParamFloat.
GType: enter g_type_class_ref() times = 58.
GType: current type: GParamBoxed
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 59.
GType: current type: GParamBoxed
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 60.
GType: current type: GParamBoxed
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 61.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 62.
GType: current type: GParamInt
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 63.
GType: current type: GParamInt
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 64.
GType: current type: GParamInt
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 65.
GType: current type: GParamInt
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 66.
GType: current type: GParamString
GType: current type already exist. return.
GType: Node name: GtkContainer.
GType: enter g_type_class_ref() times = 67.
GType: current type: GtkResizeMode
GType: type_data_ref_Wm(GtkResizeMode).
GType: find current type's GtkResizeMode parent: GEnum.
GType: enter g_type_class_ref() times = 68.
GType: current type: GEnum
GType: current type already exist. return.
GType: Node name: GtkResizeMode.
GType: enter g_type_class_ref() times = 69.
GType: current type: GParamEnum
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 70.
GType: current type: GParamUInt
GType: type_data_ref_Wm(GParamUInt).
GType: find current type's GParamUInt parent: GParam.
GType: enter g_type_class_ref() times = 71.
GType: current type: GParam
GType: current type already exist. return.
GType: Node name: GParamUInt.
GType: enter g_type_class_ref() times = 72.
GType: current type: GParamObject
GType: current type already exist. return.
GType: Node name: GtkBin.
GType: Node name: GtkWindow.
GType: enter g_type_class_ref() times = 73.
GType: current type: GtkWindowType
GType: type_data_ref_Wm(GtkWindowType).
GType: find current type's GtkWindowType parent: GEnum.
GType: enter g_type_class_ref() times = 74.
GType: current type: GEnum
GType: current type already exist. return.
GType: Node name: GtkWindowType.
GType: enter g_type_class_ref() times = 75.
GType: current type: GParamEnum
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 76.
GType: current type: GParamString
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 77.
GType: current type: GParamString
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 78.
GType: current type: GParamString
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 79.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 80.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 81.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 82.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 83.
GType: current type: GtkWindowPosition
GType: type_data_ref_Wm(GtkWindowPosition).
GType: find current type's GtkWindowPosition parent: GEnum.
GType: enter g_type_class_ref() times = 84.
GType: current type: GEnum
GType: current type already exist. return.
GType: Node name: GtkWindowPosition.
GType: enter g_type_class_ref() times = 85.
GType: current type: GParamEnum
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 86.
GType: current type: GParamInt
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 87.
GType: current type: GParamInt
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 88.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 89.
GType: current type: GParamObject
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 90.
GType: current type: GParamString
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 91.
GType: current type: GParamObject
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 92.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 93.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 94.
GType: current type: GdkWindowTypeHint
GType: type_data_ref_Wm(GdkWindowTypeHint).
GType: find current type's GdkWindowTypeHint parent: GEnum.
GType: enter g_type_class_ref() times = 95.
GType: current type: GEnum
GType: current type already exist. return.
GType: Node name: GdkWindowTypeHint.
GType: enter g_type_class_ref() times = 96.
GType: current type: GParamEnum
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 97.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 98.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 99.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 100.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 101.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 102.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 103.
GType: current type: GParamBoolean
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 104.
GType: current type: GdkGravity
GType: type_data_ref_Wm(GdkGravity).
GType: find current type's GdkGravity parent: GEnum.
GType: enter g_type_class_ref() times = 105.
GType: current type: GEnum
GType: current type already exist. return.
GType: Node name: GdkGravity.
GType: enter g_type_class_ref() times = 106.
GType: current type: GParamEnum
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 107.
GType: current type: GParamObject
GType: current type already exist. return.
GType: enter g_type_class_ref() times = 108.
GType: current type: GParamDouble
GType: type_data_ref_Wm(GParamDouble).
GType: find current type's GParamDouble parent: GParam.
GType: enter g_type_class_ref() times = 109.
GType: current type: GParam
GType: current type already exist. return.
GType: Node name: GParamDouble.
GType: enter g_type_class_ref() times = 110.
GType: current type: GtkWindow
GType: current type already exist. return.
GObject: g_type_name (object_type) : GtkStyle
GObject: class == NULL.
GType: enter g_type_class_ref() times = 111.
GType: current type: GtkStyle
GType: type_data_ref_Wm(GtkStyle).
GType: find current type's GtkStyle parent: GObject.
GType: enter g_type_class_ref() times = 112.
GType: current type: GObject
GType: current type already exist. return.
GType: Node name: GtkStyle.
GType: enter g_type_class_ref() times = 113.
GType: current type: GtkStyle
GType: current type already exist. return.
(process:802): GLib-GObject-WARNING **: invalid (NULL) pointer instance
(process:802): GLib-GObject-CRITICAL **: g_signal_connect_data: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed
|
`void
|
`GInterface
|
+GTypePlugin
|
+AtkImplementorIface
|
`GtkBuildable
|
`gchar
|
`guchar
|
`gboolean
|
`gint
|
`guint
|
`glong
|
`gulong
|
`gint64
|
`guint64
|
`GEnum
|
+GdkExtensionMode
|
+GtkTouchSoundType
|
+GtkSvibeEffectType
|
+GtkStateType
|
+GtkTextDirection
|
+GtkDirectionType
|
+GtkDragResult
|
+GtkWidgetHelpType
|
+GtkResizeMode
|
+GtkWindowType
|
+GtkWindowPosition
|
+GdkWindowTypeHint
|
`GdkGravity
|
`GFlags
|
`GdkEventMask
|
`gfloat
|
`gdouble
|
`gchararray
|
`gpointer
|
`GType
|
`GBoxed
|
+GValueArray
|
+GtkRequisition
|
+GdkRectangle
|
+GdkEvent
|
+GtkSelectionData
|
+GdkColor
|
`GtkBorder
|
`GParam
|
+GParamChar
|
+GParamUChar
|
+GParamBoolean
|
+GParamInt
|
+GParamUInt
|
+GParamLong
|
+GParamULong
|
+GParamInt64
|
+GParamUInt64
|
+GParamUnichar
|
+GParamEnum
|
+GParamFlags
|
+GParamFloat
|
+GParamDouble
|
+GParamString
|
+GParamParam
|
+GParamBoxed
|
+GParamPointer
|
+GParamValueArray
|
+GParamObject
|
+GParamOverride
|
`GParamGType
|
`GObject
|
+GInitiallyUnowned
| |
| `GtkObject
| |
| `GtkWidget
| |
| `GtkContainer
| |
| `GtkBin
| |
| `GtkWindow
|
+GtkStyle
|
+GdkDrawable
| |
| `GdkWindow
|
+GdkDragContext
|
+GtkTooltip
|
+GdkScreen
|
+GtkDeviceGroup
|
`GdkPixbuf
- 看看GtkWindow如何被生出来的(gtk_window_new()的内部机制).
- 看看GtkWindow如何被生出来的(gtk_window_new()的内部机制)
- 牛生牛的问题,假如生出来的都是母牛
- 一头母牛,三年后,这头母牛每年会生出1头母牛, 生出来的母牛三年后,又可以每年生出一头母牛
- 孕晚期如厕的那些事儿,担心一用劲把宝宝生出来了!
- 基因牛 张教授采用基因干预技术成功培养出一头母牛,三年后,这头母牛每年会生出1头母牛, 生出来的母牛三年后,又可以每年生出一头母牛。
- strcpy的内部机制
- LocalActivityManager的内部机制
- 线程的内部机制
- LocalActivityManager的内部机制
- ThreadLocal的内部机制
- Activity的内部机制
- PendingIntent的内部机制
- LocalActivityManager的内部机制
- PendingIntent的内部机制
- PendingIntent的内部机制
- Window的内部机制
- Hashmap的内部机制
- UIView的一些基本方法 init、loadView、viewDidLoad、viewDidUnload、dealloc
- poj 3461 Oulipo(KMP)
- 相机变换矩阵
- DIY一个C++ traits来判断enum是否有用户自定义的operator<<
- 开篇之坚持...
- 看看GtkWindow如何被生出来的(gtk_window_new()的内部机制)
- RS232与RS485的功能与区别!
- 从getaddrinfo看Glibc的nss
- WorkerThread
- vm 下安装linux系统自动安装无法配置的解决方法
- 去重
- SQL Server 2005 中的数据类型小结
- 并查集
- 生成QR Code 二维条形码 ----- Google Chart API