早期 Ruby 版本中的 jFrozen 字符串文字



这个问题特别适用于Ruby 1.9和2.1,其中字符串文字不能自动冻结。特别是我指的是这篇文章,它建议冻结字符串,这样对代码的重复评估就不会每次都创建一个新的 String 对象,据说这在其他优点中使程序性能更好。作为一个具体的例子,本文提出了表达式

("%09d".freeze % id).scan(/d{3}/).join("/".freeze)

我想在我们的项目中使用这个概念,出于测试目的,我尝试了以下代码:

3.times { x="abc".freeze; puts x.object_id }

在 Ruby 2.3 中,每次都会打印相同的对象 ID。在 JRuby 1.7 中,它在语言级别对应于 Ruby 1.9,它打印了三个不同的对象 ID,尽管我已经明确冻结了字符串。

有人可以解释一下原因,以及如何在这种情况下正确使用freeze吗?

特别是我指的是这篇文章,它建议冻结字符串,以便对代码的重复计算不会每次都创建新的 String 对象

这不是Object#freeze所做的。顾名思义,它"冻结"了对象,即它不允许对对象的内部状态进行任何进一步的修改。文档中没有任何内容甚至远程表明Object#freeze执行某种重复数据消除或实习。

你可能正在考虑String#-@,但这在Ruby 2.1中不存在。它只是在 Ruby 2.3 中添加的,实际上当时有不同的语义:

  • Ruby 2.3–2.4:如果self已经冻结,则返回self,否则返回self.dup.freeze,即字符串的冻结副本

    -strstr(冷冻(

    如果字符串被冻结,则返回字符串本身。
    如果字符串未冻结,则复制字符串将其冻结并返回。

  • Ruby 2.5+:如果self已经冻结,则返回self,否则返回重复数据删除的字符串的冻结版本(即,可以在现有冻结字符串的缓存中查找它,并返回现有版本(:

    -strstr(冷冻(

    返回冻结的、可能预先存在的字符串副本。
    只要字符串未被污染,或者在其上设置了任何实例变量,就会对其进行重复数据删除。

因此,您链接到的文章在三个方面是错误的:

仅对字符串执行重复数据消除,
  1. 而不对任意对象执行重复数据消除。
  2. 重复数据消除不是由 执行的freeze执行的。
  3. 重复数据消除仅由从 Ruby 2.5 开始的String#-@执行。

那篇文章中还有第四种说法是错误的,尽管我们不能因此而责怪作者,因为这篇文章是 2016 年的,而且决定只是在 2019 年才改变:Ruby 3.0 默认情况下不会有不可变的字符串文字。

该文章中正确的一件事是,# frozen_string_literal: true编译指示(或相应的命令行选项--enable-frozen-string-literal(不仅会冻结所有静态字符串文字,还会删除重复数据。

相关内容

  • 没有找到相关文章

最新更新