Ruby中常量是可变的
准确的说,Ruby中的常量字符串是可变,如:
MY_CONSTANT = "foo"
MY_CONSTANT << "bar"
puts MY_CONSTANT.inspect # 输出 "foobar"
如果对MY_CONSTANT调用freeze方法,便可创建一个真正的常量字符串,如果尝试修改,则会抛出异常,如:
MY_CONSTANT = "foo".freeze
MY_CONSTANT << "bar" # 抛出 FrozenError (cannot modify frozen string)
Freeze字符串的好处
最明显的好处就是可以节省内存,提高程序运行效率。每次Ruby解释器遇到 "foobar"都会创建新的字符串。想象一下,在一个线上的应用里,如果创建大量的内容一致的字符串,不仅占用大量内存,还会造成频繁的GC,拖慢程序的运行。
解释器优化
从Ruby 2.2开始,MRI会对作为hash键的字符串自动做freeze处理。
从Ruby 2.3开始,提供了2种方式设置字符创为不可变:
- 在文件的顶部添加
# frozen_string_literal: true - 命令行添加选项
--enable=frozen_string_literal
从Ruby 3.0开始,所有的字符串都会是不可变的。
let it go
let_it_go是一个帮助我们检查项目中潜在的可以被freeze的开源工具。
Freeze对象
在自定义类中,在构造函数里调用freeze,可以保证创建的实例对象是不可改变的,如:
class Point
attr_accessor :x, :y
def initialize(x, y)
@x = x
@y = y
freeze
end
def change
@x = 3
end
end
point = Point.new(1,2)
point.change # 抛异常,in `change': can't modify frozen Point (FrozenError)