Ruby 是一个完全的面向对象 ( OOP ) 的编程语言,哪怕是一个普通的 nil
值,背后也存在着一个类 NilClass
。本章节,我们就来探究下 Ruby 中的 nil
值和它背后的实现,我们将涉及到以下几个问题
nil
值NilClass
类nil?
方法实现
nil 值
我们首先来探寻下 nil
值。
Ruby 中的 nil
值的含义是用于表示 「 缺失对象 」的概念
nil
值也被垃圾收集器使用 - 它实现了标记和扫描算法 - 来决定是否应该销毁一个对象
与其他语言不同,Ruby 中的 nil
值并不用于表示空字符串 ''
,false
或 0 值。
由于 Ruby 中的所有内容都是对象,因此 nil 值它属于一个类,一个不可实例化的 NilClass
类
NilClass 类
NilClass
是 Ruby 的内置 ( built-in ) 类。此类不可实例化。
irb> NilClass.new NoMethodError (undefined method `new' for NilClass:Class)
当一个消息发送到 nil
时,会有一个名为 rb_cNilClass
的硬编码 C 语言类( 对应于 Ruby 中的NilClass )被用作消息的接收者。
然后将在新接收器的实例方法中找到和发送消息
因此,nil 更像是关键字而不是实例。因为它只是表达 「 缺失对象 」概念的一种方式
另一方面,NilClass 封装了所有逻辑,并在消息的接收者为 nil
时使用
nil? 方法
调用 nil?
方法可用于确定接收器是否为一个对象
irb> "I'm an object".nil? => false irb> a = nil => nil irb> a.nil? => true
很简单,不是吗?
你是不是跟我一样好奇,这个 nil?
方法在内部是如何实现的。那么,我们就一起来看看 Ruby 源代码吧。
Ruby 是用 C 语言 编写的。但是,不要惊慌,如果你不熟悉这种编程语言,也没关系,因为我们看的代码非常简单
// in ruby/object.c void InitVM_Object(void) { ... rb_define_method(rb_mKernel, "nil?", rb_false, 0); ... rb_define_method(rb_cNilClass, "nil?", rb_true, 0); ... }
Ruby 虚拟机 ( VM ) 启动期间会调用 InitVM_Object
函数。此函数负责设置与 Object 类相关的所有环境和类
rb_define_method(klass,message,function,argc)
用于向模块/类添加消息并将此消息链接到 C 语言级别的函数。
因此,上面的代码中,rb_define_method(rb_mKernel,“nil?”,rb_false,0);
就是把 nil?
消息添加到内核模块并将此消息链接到 rb_false
函数
rb_define_method
方法的最后一个参数是传递给给方法的参数数量 - 这里没有预期的参数,所以是 0
由于 Kernel
模块包含在 Object 类中,而 Object 类是 Ruby 中所有类的默认父类,因此,几乎所有类都可以处理 nil?
方法
rb_false 方法
接下来,我们来看看 rb_false
方法是如何实现的。
static VALUE rb_false(VALUE obj) { return Qfalse; }
rb_false
函数返回一个 Qfalse
- 这是 Ruby C 语言级别的 false
值。
这就意味着,默认情况下,所有的对象都不是 nil
最后,我们通过 rb_true 函数来看看 NilClass#nil
的实现
static VALUE rb_true(VALUE obj) { return Qtrue; }
rb_true
函数返回一个 Qtrue
- 这是 Ruby C 语言级别的 true
值。
这意味着 NilClass#nil?
方法重写了 Kernel#nil?
方法 (返回 false ) 使其返回 true
这是一个聪明的实现,通过使用继承来特殊化 NilClass
类来遵守 OOP 原则