我们可以从很多著名的存储库中轻松找到这种样式,例如机架、导轨等。
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
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 会打印警告,这是正确的:
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
通常 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 进程。