为什么到处都有冻结常数?

我们可以从许多着名的存储库中轻松找到这种风格,如机架,导轨等。

例如在机架中 :

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 

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会发出警告是正确的:

 > 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 # => "fo" 

freeze允许保护常量的值不被改变。 freeze示例:

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

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

 log("debug") 

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

但如果文字被冻结了

 log("debug".freeze) 

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

来自ruby3.0的ruby会冻结每根绳子, – 根据马茨的说法。


更新:

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

 # frozen_string_literal: true 

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

一个解释为什么你看到流行项目中常量冻结的常数是他们使用代码分析器Rubocop 。

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