【问题标题】:Why are there frozen constants everywhere?为什么到处都有冻结常数?
【发布时间】:2015-02-26 10:42:42
【问题描述】:

我们可以很容易地从许多著名的存储库中找到这种风格,例如 rack、rails 等。

For example in rack:

PATH_INFO      = 'PATH_INFO'.freeze
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
SCRIPT_NAME    = 'SCRIPT_NAME'.freeze
QUERY_STRING   = 'QUERY_STRING'.freeze
CACHE_CONTROL  = 'Cache-Control'.freeze
CONTENT_LENGTH = 'Content-Length'.freeze
CONTENT_TYPE   = 'Content-Type'.freeze

Another examle in rails:

HTTP_IF_MODIFIED_SINCE = 'HTTP_IF_MODIFIED_SINCE'.freeze
HTTP_IF_NONE_MATCH     = 'HTTP_IF_NONE_MATCH'.freeze
HTTP_IF_NONE_MATCH     = 'HTTP_IF_NONE_MATCH'.freeze

我想知道为什么这些常量字符串会被冻结。由于它们都是常量,因此应该只有一个实例。当然,我们可以将 "foo".freeze 放在某个地方来引用同一个单例实例,但是人们通常会写成像 HTTP_IF_MODIFIED_SINCE 这样的字面变量名。

所以在我看来,尽管使用#freeze 并没有什么区别,那么为什么人们要冻结常量呢?

【问题讨论】:

  • 阻止人们意外变异它们?

标签: ruby-on-rails ruby ruby-on-rails-3 ruby-on-rails-4 rack


【解决方案1】:

当你为一个已经初始化的常量重新赋值时,Ruby 打印一个警告是正确的:

> FOO = 'foo'
> FOO = 'bar'
# :2: warning: already initialized constant FOO
# :1: warning: previous definition of FOO was here
> FOO
# => "bar"

但是没有保护可以防止改变常量中的值。没有freeze的例子:

> FOO = 'foo'
> FOO[1] = '-'
> FOO
# => "f-o"

但是freeze 允许保护常量的值不被更改。以freeze 为例:

> FOO = 'foo'.freeze
> FOO[1] = '-'
# => RuntimeError: can't modify frozen String

【讨论】:

  • 感谢您的回答!可笑的是我记得String#freeze的神奇效果,却忘记了#freeze的初衷……orz
  • 我相信 Rack and Rails 的主要原因是性能相关。有关使用 String#freeze 的优化,请参阅 Richard Schneeman 的 this article
【解决方案2】:

通常 Rubyist 冻结字符串文字以加快执行速度。如果在某个控制器中有一些函数调用,例如如下所示,每个请求都会调用该函数。

log("debug")

发生的事情是 ruby​​ 每次都定义一个新的垃圾字符串对象。对象分配不是免费的。它消耗内存和CPU。垃圾会一直存在,直到 GC 收集它们。

但如果文字被冻结

log("debug".freeze)

ruby 分配一次并缓存它以供以后使用。此外,字符串对象将是不可变的,并且可以安全地在多线程环境中使用。

根据 Matz 的说法,从 ruby​​ 3.0 开始,ruby 将冻结每个字符串。


更新:

如果您在 ruby​​ 文件的开头添加以下注释,则整个文件中的每个字符串文字都将是不可变的。当您尝试针对多线程环境优化应用时,这非常有用。

# frozen_string_literal: true

或者您甚至可以使用--enable-frozen-string-literal 开关启动您的Ruby 进程。

【讨论】:

    【解决方案3】:

    为什么您会在流行项目中看到这种持续冻结常量的一种解释是,它们使用代码分析器 Rubocop

    由于@spickermann 上面提到的原因,这是一个标准的 Rubocop rule,常量不应该是可变的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多