2

Fixnum我在doc中找到了以下功能。

Fixnum对象具有直接价值。这意味着当它们被分配或作为参数传递时,传递的是实际对象,而不是对该对象的引用。

可以在 IRB 中显示相同的内容吗?希望只有它会被我正确理解?

赋值不对 Fixnum 对象起别名。

那它实际上是什么?

对于任何给定的整数值,实际上只有一个 Fixnum 对象实例,因此,例如,您不能将单例方法添加到 Fixnum.

无法理解不使用对象实例添加singleton方法的原因。Fixnum

我对第二点进行了一些尝试,如下所示:

a = 1                      # => 1
a.class                    # => Fixnum
b = a                      # => 1
b.class                    # => Fixnum
a == b                     # => true
a.equal? b                 # => true
a.object_id == b.object_id # => true

但我仍然很困惑。任何人都可以在这里帮助我了解这些功能的核心吗?

4

2 回答 2

11

在 Ruby 中,大多数对象都需要内存来存储它们的类和实例变量。一旦分配了这个内存,Ruby 就通过这个内存位置来表示每个对象。当对象被赋值给一个变量或传递给一个函数时,传递的是这个内存的位置,而不是这个内存中的数据。单例方法利用了这一点。当你定义一个单例方法时,Ruby 会默默地用一个新的单例类替换对象类。因为每个对象都存储它的类,Ruby 可以很容易地用一个实现单例方法(并继承自原始类)的新类替换对象的类。

对于立即值的对象,这不再是正确的:truefalsenil、所有符号和小到足以容纳在 Fixnum 中的整数。Ruby 不会为这些对象的实例分配内存,它不会在内部将对象表示为内存中的位置。相反,它根据对象的内部表示来推断对象的实例。这意味着双重:

  1. 每个对象的类不再存储在内存中的特定位置,而是由直接对象的类型隐式确定。这就是为什么 Fixnums 不能有单例方法的原因。

  2. 具有相同状态的立即对象(例如,整数 2378 的两个 Fixnums)实际上是相同的实例。这是因为实例是由这个状态决定的。

为了更好地理解这一点,请考虑对 Fixnum 进行以下操作:

>> x = 3 + 7
=> 10
>> x.object_id == 10.object_id
=> true
>> x.object_id == (15-5).object_id
=> true

现在,考虑使用字符串:

>> x = "a" + "b"
=> "ab"
>> x.object_id == "ab".object_id
=> false
>> x.object_id == "Xab"[1...3].object_id
=> false
>> x == "ab"
=> true
>> x == "Xab"[1...3]
=> true

Fixnums 的对象 id 相等的原因是它们是具有相同内部表示的直接对象。另一方面,字符串存在于分配的内存中。每个字符串的对象 id 是其对象状态在内存中的位置。

一些底层信息

要理解这一点,您必须了解 Ruby(至少 1.8 和 1.9)在内部如何处理 Fixnums。在 Ruby 中,所有对象在 C 代码中都由类型为 的变量表示VALUE。Ruby 对 施加以下要求VALUE

  1. 类型 VALUE 是足以容纳指针的最小整数。这意味着,在 C 中,sizeof(VALUE) == sizeof(void*).

  2. 任何非立即对象必须在 4 字节边界上对齐。这意味着 Ruby 分配的任何对象都将具有4*i某个整数的地址i。这也意味着所有指针在它们的两个最低有效位中都有零值。

第一个要求允许 Ruby 将指向对象的指针和立即值存储在类型变量中VALUE。第二个要求允许 Ruby 根据两个最低有效位检测 Fixnum 和 Symbol 对象。

为了更具体,考虑 Ruby 对象的内部二进制表示z,我们将Rz在 32 位架构中调用它:

MSB                                   LSB
  3           2            1            
 1098 7654 3210 9876 5432 1098 7654 32 10
 XXXX XXXX XXXX XXXX XXXX XXXX XXXX AB CD

Ruby 然后解释Rz, 的表示z,如下:

  1. 如果D==1z则为 Fixnum。该 Fixnum 的整数值存储在表示的高 31 位中,并通过执行算术右移以恢复存储在这些位中的有符号整数来恢复。

  2. 测试了三种特殊表示(全部带有D==0

    • 如果Rz==0, 那么zfalse
    • 如果Rz==2, 那么ztrue
    • 如果Rz==4, 那么znil
  3. 如果ABCD == 1110,那么 'z' 是一个符号。该符号通过右移八个最低有效位(即,z>>8在 C 中)转换为唯一 ID。在 32 位架构上,这允许 2^24 个不同的 ID(超过 1000 万个)。在 64 位架构上,这允许 2^48 个不同的 ID。

  4. 否则,Rz表示 Ruby 对象实例在内存中的地址,其类型z由该位置的类信息确定。

于 2013-03-07T01:15:30.047 回答
1

好...

  1. 这是 MRI 的内部实现细节。您会看到,在 Ruby 中(在 C 方面),所有 Ruby 对象都是VALUE. 在大多数情况下,aVALUE是指向堆栈中的对象的指针。但是直接值(Fixnums、true、false、nil 和其他)存在于VALUE指向对象的指针通常存在的地方。
  2. 它使变量成为完全相同的对象。现在,我不知道这是如何在内部工作的,因为它会将其分配给VALUE自身,但确实如此。
  3. 因为每次我1在程序中使用数字时,我都使用同一个对象。因此,如果我在 上定义一个单例方法1,我的程序中的每个地方、所需的程序等都会有该单例方法。单例方法通常用于本地猴子补丁。所以,为了防止这种情况,Ruby 不允许你这样做。
于 2013-03-06T14:52:20.863 回答