美文网首页ruby on rails
Ruby的方法和常量查找

Ruby的方法和常量查找

作者: falm | 来源:发表于2017-04-20 19:22 被阅读395次

Ruby是一门单一继承的面向对象语言,那么在内部结构上,它是以object为根节点的树形结构的类图,那么我们在Ruby中定义的方法和常量也,依附在这个结构之上的,那么方法和常量是如何被定为查找的呢,我们要从模块说起。

模块

类在Ruby的内部结构中,是使用RClass结构体来表示的,但是模块不是使用类似,RModule这种结构实现的,而是同样的使用了RClass,这样的结构看起来非常的简洁,在内部保持了一致性,为方法和常量查找算法提供了简便的基础。

模块在内部同样使用了 RClassrb_classext_struct 两个结构体,所以说模块在某种程度上说也是类的一种,我们看到上图的结构体中,较以往的RClass结构略有不同,因为模块不需要实例化,所以也就去掉了一下与实例相关的结构,可以说Ruby的模块就是包含方法定义,常磊指针和常量表的Ruby对象。

那么模块在内部是怎么被添加到类中的呢。

module Professor
end
class Mathematican < Person
  include Professor
end

Ruby是在模块Professor的RClass的基础上做了一个副本,然后将这个副本作为Mathematican类的超类,这种形式将Professor模块添加到了Mathematican类的继承链上。

ruby_method_lookup.png

从上图我们就可以看出来,方法查找的整个过程是一目了然的简单,但是如果每次的方法调用都要经过,整个树形结构的遍历的话,效率不是很好,所以Ruby在方法查找的功能中添加了全局方法缓存和内联方法缓存这两个缓存,来保证方法查找的效率。

  • 全局方法缓存,是用于保存接收者和实现类之间的映射表,Ruby在第一次方法查找之后就会将查找链路添加的映射表中,当第二次查找该方法的时候就可以直接使用映射表中的目标类了。
  • 内联方法缓存,是将方法执行的YARV指令直接进行缓存的工具,这样在方法的第二次查找是可以直接的执行YARV指令,进一步提升速度。

有缓存就有缓存的失效机制,两个方法查找缓存,都是在Ruby创建和清除方法或者是include模块的时候进行缓存清除的。

Prepend

类中引入两个模块的时候,Ruby的方法查找是按照模块引入的顺序进行查找的,后引入的模块会在,继承链的倒数第二的位置上。那么Ruby模块的prepend方法的查找又是如果进行的呢,下面的代码中 模块和类都定义了name 方法,那么最后方法调用的时候,调用的会是Mathematician类属性构造器定义的name方法。

module Professor
   def name
        "Prof. #{super}"
   end
end
class Mathematician
   attr_accessor :name
   include Professor
end
m = Mathematician.new
m.name = 'Henri'
p m.name #=> Henri

如果我们想要让Professor模块中的方法重载类中的同名方法,就需要使用prepend修改一个例子了。

module Professor
  def name
    "Prof. #{super}"
  end
end
class Mathematician
  attr_accessor :name
  prepend Professor
end
m = Mathematician.new
m.name = 'Henri'
p m.name  #=> Prof. Henri

那么prepend是如何做到重载类中的方法的呢,其实秘密就是Ruby内部使用了一个小技巧,在使用prepend时,Ruby会在内部的创建目标类的副本(在内部叫原生类 origin class) 并且把它设置成前置模块的超类,Ruby使用了rb_classext_struct结构体中的origin指针来记录该类的原生副本,这样在方法查找的时候,就会先找到prepend模块的方法。

常量查找

在Ruby中常量不仅仅用于表示不可变值,它还是Ruby类和模块的引用对象,也就是类和模块的名字都是常量,那么常量查找的其实就是查找类和模块。

常量本身是存放在RClass 结构体的constants常量表中的,普通的常量查找是通过和方法查找同样的方式进行的,首先是先在本类的常量表中查找常量,如果没有找到的话在到父类的常量表中查找。

class MyClass
  SOME_CONSTANT = "Some value..."
end
class Subclass < MyClass 
  p SOME_CONSTANT
end

词法作用域

上面说到了,Ruby是如何在本类和祖先类中查找常量的,但是在模块实际的使用当中,模块的命名空间常量查找又是如何进行的呢,这里就要提到Ruby在父级空间查找常量的词法作用域问题了。

Ruby的词法作用域,有作用域门控制,也就是 module 或者 class 这样的关键字定义的作用域,还有就是诚信的默认『顶级作用域』在不同的作用域中为了定位程序代码的位置,需要使用一对指针来对应作用域内的YARV指令片段。

  • nd_next 指针,被设置为父层或上下文的词法作用域。
  • nd_class 指针,表示Ruby类或模块对应的作用域。

有了上面的作用域结构,常量查找的算法也就变得简单了。

ruby_constants_lookup.png

我们上面说到了,Ruby常量查找的两种方式,但是在真实的常量查找中是先使用哪种方式呢,简单的说Ruby或先使用词法作用域查找常量,如果没有找到的话再使用超类链查找常量,注意这里的词法作用域查找在真实的使用场景下,不仅仅是上图所示,它还会在父词法作用域中查找autoload关键字。

ruby_cons_lp.png

相关文章

  • Ruby的方法和常量查找

    Ruby是一门单一继承的面向对象语言,那么在内部结构上,它是以object为根节点的树形结构的类图,那么我们在Ru...

  • Ruby 中的常量查找

    在 Ruby 中,当你需要访问一个常量的时候,很简单直接使用这个常量的名字就行。但是你知道 Ruby 是如何查找一...

  • From Objective-C to Ruby(0)-常量变量

    变量 & 常量 OC: ruby: 注释 OC: ruby:

  • 查看ruby api

    在ruby中,以问号结尾的方法往往返回的是true或者false Ruby中的双冒号要么表示常量要么表示命名空间下...

  • ruby中方法的调用——方法查找

    调用方法时,ruby会做两件事: 1、找到这个方法,这个过程称为方法查找 2、执行这个方法, 当前对象由self充...

  • ruby扩展类及模块的知识

    Ruby中的模块可以说是方法的集合,各种的集合我们之前调用类中的常量是类名::常量名称,如Student::Ver...

  • Ruby--变量和常量

    局部变量:以英文字母或_开头 全局变量:以$开头 实例变量:以@开头 类变量:以@@开头 常量:以英文大写字母开头...

  • From Objective-C to Ruby(4)-类和模块

    类 定义类 OC: ruby: 初始化方法 OC: ruby: 实例变量和属性 OC: ruby: 类方法和对象方...

  • Ruby的动态特性

    Ruby动态特性的体现 动态执行字符串形式的代码。 动态获取模块或类中的常量和变量值 动态为类或者对象添加方法 对...

  • ruby中String和Symbol区别

    在ruby中Symbol的方法很少,占得空间很少,同一个常量只用一个内存地址。 String: Symbol: 可...

网友评论

    本文标题:Ruby的方法和常量查找

    本文链接:https://www.haomeiwen.com/subject/funaattx.html