你不能改变 self 的值
对象是class pointer and a set of instance methods(请注意,此链接是旧版本的 Ruby,因为它非常简单,因此更便于说明)。
“指向”一个对象意味着您有一个变量,该变量将对象的位置存储在内存中。然后对对象做任何事情,你首先去内存中的位置(我们可以说“跟随指针”)来获取对象,然后做事情(例如调用一个方法,设置一个ivar)。
所有 Ruby 代码都在某个对象的上下文中执行。这是保存实例变量的地方,也是 Ruby 查找没有接收器的方法的地方(例如,$stdout 是 $stdout.puts "hi" 中的接收器,当前对象是 puts "hi" 中的接收器)。有时你需要对当前对象做一些事情。处理对象的方式是通过变量,但是当前对象指向什么变量呢?没有一个。为了满足这一需求,提供了关键字self。
self 就像一个变量,它指向当前对象的位置。但它不像变量,因为你不能给它赋新值。如果可以的话,那之后的代码会突然在不同的对象上运行,这会令人困惑,并且与仅使用变量相比没有任何好处。
还要记住,对象由存储内存地址的变量跟踪。 self = 2 应该是什么意思?这是否仅仅意味着当前代码的运行就像调用了2 一样?或者这是否意味着所有指向旧对象的变量现在都更新了它们的值以指向新对象?目前还不是很清楚,但前者不必要地引入了身份危机,而后者则过于昂贵,并引入了不清楚什么是正确的情况(我将在下面进一步讨论)。
你不能改变 Fixnums
Some objects 在 Ruby 中的 C 级别是特殊的(false、true、nil、fixnums 和符号)。
指向它们的变量实际上并不存储内存位置。相反,地址本身存储了对象的类型和身份。无论在哪里,Ruby 都会检查它是否是一个特殊对象(例如 looking up an instance variable 时),然后从中提取值。
所以内存中没有存储对象123 的位置。这意味着 self 包含 Fixnum 123 的概念,而不是像往常一样的内存地址。与变量一样,它会在必要时进行检查和特殊处理。
因此,您无法改变对象本身(尽管它们似乎保留了a special global variable 以允许您在符号之类的东西上设置实例变量)。
他们为什么要这么做?为了提高性能,我假设。存储在寄存器中的数字只是一系列位(通常为 32 或 64),这意味着存在用于加法和乘法之类的硬件指令。也就是说,ALU 被连线以在单个时钟周期内执行这些操作,而不是用软件编写算法,这将花费许多数量级的时间。通过这样存储它们,它们避免了在内存中存储和查找对象的成本,并且它们获得了可以使用硬件直接添加两个指针的优势。但是请注意,在 Ruby 中仍有一些额外的成本,而在 C 中是没有的(例如检查溢出并将结果转换为 Bignum)。
爆炸方法
您可以在任何方法的末尾加上一个 bang。它不需要改变对象,只是人们通常会在你做一些可能会产生意想不到的副作用的事情时尝试警告你。
class C
def initialize(val)
@val = val # => 12
end # => :initialize
def bang_method!
"My val is: #{@val}" # => "My val is: 12"
end # => :bang_method!
end # => :bang_method!
c = C.new 12 # => #<C:0x007fdac48a7428 @val=12>
c.bang_method! # => "My val is: 12"
c # => #<C:0x007fdac48a7428 @val=12>
另外,整数没有 bang 方法,它不符合范式
Fixnum.instance_methods.grep(/!$/) # => [:!]
# Okay, there's one, but it's actually a boolean negation
1.! # => false
# And it's not a Fixnum method, it's an inherited boolean operator
1.method(:!).owner # => BasicObject
# In really, you call it this way, the interpreter translates it
!1 # => false
替代方案
此外,当您提出此类问题时,您应该展示您的代码。我误解了你很长一段时间是如何接近它的。