首页 测试 工具 查看内容

Ruby的模块包含机制

2014-9-4 16:50| 发布者: | 查看: 481| 评论: 0

摘要:   这个函数比较简单,除了类型安全检查,它只是 rb_include_module 函数的封装,我们进一步跟到 rb_include_module 函数中去看看。voidrb_include_module(VALUE klass, VALUE module){int changed = 0;rb_frozen_c ...
voidrb_include_module(VALUE klass, VALUE module){int changed = 0;rb_frozen_class_p(klass);if (!OBJ_UNTRUSTED(klass)) {rb_secure(4);}if (!RB_TYPE_P(module, T_MODULE)) {Check_Type(module, T_MODULE);}OBJ_INFECT(klass, module);changed = include_modules_at(klass, RCLASS_ORIGIN(klass), module);if (changed < 0)rb_raise(rb_eArgError, "cyclic include detected");if (changed) rb_clear_cache();}  同样,rb_include_module 函数也没有做多少实际的工作,只是做了一些安全方面的检查,然后把主要工作交给了 include_modules_at 函数。static intinclude_modules_at(const VALUE klass, VALUE c, VALUE module){VALUE p;int changed = 0;const st_table *const klass_m_tbl = RCLASS_M_TBL(RCLASS_ORIGIN(klass));while (module) {int superclass_seen = FALSE;if (RCLASS_ORIGIN(module) != module)goto skip;if (klass_m_tbl && klass_m_tbl == RCLASS_M_TBL(module))return -1;/* ignore if the module included already in superclasses */for (p = RCLASS_SUPER(klass); p; p = RCLASS_SUPER(p)) {switch (BUILTIN_TYPE(p)) {case T_ICLASS:if (RCLASS_M_TBL(p) == RCLASS_M_TBL(module)) {if (!superclass_seen) {c = p;  /* move insertion point */}goto skip;}break;case T_CLASS:superclass_seen = TRUE;break;}}c = RCLASS_SUPER(c) = rb_include_class_new(module, RCLASS_SUPER(c));if (FL_TEST(klass, RMODULE_IS_REFINEMENT)) {VALUE refined_class =rb_refinement_module_get_refined_class(klass);st_foreach(RMODULE_M_TBL(module), add_refined_method_entry_i,(st_data_t) refined_class);FL_SET(c, RMODULE_INCLUDED_INTO_REFINEMENT);}if (RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries)changed = 1;if (RMODULE_CONST_TBL(module) && RMODULE_CONST_TBL(module)->num_entries)changed = 1;skip:module = RCLASS_SUPER(module);}return changed;}  include_modules_at 函数做了许多工作,值得好好分析一下。   首先,最外层的 while 循环对 module 的祖先链(ancestors)逐一遍历。对于每个 module,内层的 for 循环会检查 module 是否已经包含在 klass 的祖先链中,如果已经包含,则跳过,否则,把 module 插入到 c 和 c 的父类之间。  在测试 module 是否已包含在祖先链的过程中,变量 superclass_seen 用来判断是否越过了一个非包含类,如果是,则不移动插入点。所以模块包含不会将模块插到祖先链中下一个非包含类之后。  模块的插入工作是由第 31 行代码完成的,它调用 rb_include_class_new 函数为模块创建了一个包含类(include class),它以 c 的父类作为父类,接着又将这个包含类设置为 c 的父类。我们来看看 rb_include_class_new 函数是如何创建包含类的。VALUErb_include_class_new(VALUE module, VALUE super){VALUE klass = class_alloc(T_ICLASS, rb_cClass);if (BUILTIN_TYPE(module) == T_ICLASS) {module = RBASIC(module)->klass;}if (!RCLASS_IV_TBL(module)) {RCLASS_IV_TBL(module) = st_init_numtable();}if (!RCLASS_CONST_TBL(module)) {RCLASS_CONST_TBL(module) = st_init_numtable();}RCLASS_IV_TBL(klass) = RCLASS_IV_TBL(module);RCLASS_CONST_TBL(klass) = RCLASS_CONST_TBL(module);RCLASS_M_TBL(klass) = RCLASS_M_TBL(RCLASS_ORIGIN(module));RCLASS_SUPER(klass) = super;if (RB_TYPE_P(module, T_ICLASS)) {RBASIC(klass)->klass = RBASIC(module)->klass;}else {RBASIC(klass)->klass = module;}OBJ_INFECT(klass, module);OBJ_INFECT(klass, super);return (VALUE)klass;}  第 4 行,class_alloc 创建了一个新类,并为它设置了 T_ICLASS 标识,有了这个标识,Ruby 就会认为它是一个包含类。接着就是一系列的初始化,将新类的内部表指向模块中对应的表。最后,将新类的 klass 指向模块,这样,包含类就创建完成了。  第 17 行有个 RCLASS_ORIGIN 宏,我们来看看它的作用是什么,Ruby 源码中,该宏的定义如下:  #define RCLASS_ORIGIN(c) (RCLASS_EXT(c)->origin)  可以看到,它指向一个类的 origin 成员。简单来说,它获取某个类的原始类。原始类跟模块前置有着密切的关系。关于模块前置的更多信息,请阅读《Ruby 中模块前置的实现》这篇文章。  了解这些信息之后,我们来总结一下。为了便于描述,我们以文章开头的那段代码作为例子,来回顾一下模块包含的全过程。  A = Module.new  B = Module.new  module C  include A, B    # Inclusion is done by this line  end  当调用模块 C 的 include 方法时,默认实现 Module#include 会以相反的顺序对参数模块回调 append_features 和 included 方法。真正的包含逻辑在 Module#append_features 的内部实现。  对每个被包含的模块,Ruby 会遍历其祖先链。对链上的每个模块,会首先检查是否已包含过,如果已包含,则跳过,否则为该模块创建一个包含类,插入到模块 C 的祖先链中。  当然,以上的描述省略了一些细节,比如类型安全检查、对插入点的调整、检查循环包含、以及对 Refinement 的处理。如果读者有兴趣,可以参照源代码去理解。  模块包含的本质是将被包含的模块的祖先链插入到另一个模块的祖先链中,其实最终,都是在维护一个祖先链。
  这个函数比较简单,除了类型安全检查,它只是 rb_include_module 函数的封装,我们进一步跟到 rb_include_module 函数中去看看。

鲜花

握手

雷人

路过

鸡蛋

扫一扫关注最新动态

毒镜头:老镜头、摄影器材资料库、老镜头样片、摄影
爱评测 aipingce.com  
返回顶部