2014-12-30 42 views
13

我们可以很容易地找到许多著名存储库这样的风格,像架,导轨等为什么到处冻结常量?

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,但它并没有什么区别,所以人们为什么要冻结常量?

+5

阻止人们意外突变呢? –

回答

21

这是正确的,红宝石打印时发出警告的值重新分配给已初始化的常量:

> 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 
+1

谢谢你的回答!我记得'字符串#freeze'的魔法效果,但忘了'#freeze'初衷这是荒谬的...... Orz的 –

+3

我相信,在机架和Rails的主要原因是性能相关的。参见[这篇文章](http://www.sitepoint.com/unraveling-string-key-performance-ruby-2-2/)从理查德Schneeman关于使用'字符串#freeze'优化 –

2

通常Rubyist冻结字符串文字以使执行速度更快。如果在某个控制器中有一些像下面这样的函数调用,每个请求都会调用该函数。

log("debug") 

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

但如果文字被冻结

log("debug".freeze) 

红宝石分配一次,其缓存以备后用。此外,字符串对象在多线程环境中将不可变且安全。

从红宝石3.0红宝石会冻结每一个字符串, - 根据马茨。


更新:

如果您在红宝石的开头添加下面的注释文件然后每串在整个文件文本将是不可改变的。当您试图针对多线程环境优化您的应用时,这非常有用。

# frozen_string_literal: true 

或者你甚至可以用--enable-frozen-string-literal开关启动你的Ruby进程。为什么你看到的常量的流行项目,此一致冻结

0

一种解释是,他们使用Rubocop,代码分析器。

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