102

如果我的脚本中至少有两个相同字符串的实例,我应该改用符号吗?

4

4 回答 4

187

TL;博士

一个简单的经验法则是每次需要内部标识符时都使用符号。对于 Ruby < 2.2,仅当它们不是动态生成时才使用符号,以避免内存泄漏。

完整答案

不将它们用于动态生成的标识符的唯一原因是因为内存问题。

这个问题很常见,因为许多编程语言没有符号,只有字符串,因此字符串也用作代码中的标识符。您应该担心符号的含义,而不仅仅是何时应该使用符号。符号是标识符。如果您遵循这一理念,您很可能会做正确的事情。

符号和字符串的实现有几个不同之处。符号最重要的一点是它们是不可变的。这意味着它们的价值永远不会改变。因此,符号的实例化速度比字符串快,并且比较两个符号等一些操作也更快。

符号是不可变的这一事实允许 Ruby 每次引用该符号时都使用同一个对象,从而节省内存。因此,每次解释器读取:my_key时,它都可以从内存中获取它,而不是再次实例化它。这比每次都初始化一个新字符串要便宜。

您可以获取已使用命令实例化的所有符号的列表Symbol.all_symbols

symbols_count = Symbol.all_symbols.count # all_symbols is an array with all 
                                         # instantiated symbols. 
a = :one
puts a.object_id
# prints 167778 

a = :two
puts a.object_id
# prints 167858

a = :one
puts a.object_id
# prints 167778 again - the same object_id from the first time!

puts Symbol.all_symbols.count - symbols_count
# prints 2, the two objects we created.

对于 2.2 之前的 Ruby 版本,一旦一个符号被实例化,这块内存就永远不会再空闲了。释放内存的唯一方法是重新启动应用程序。所以符号使用不当也是造成内存泄漏的主要原因。产生内存泄漏的最简单方法是使用to_sym用户输入数据的方法,因为这些数据总是会改变,内存的新部分将永远在软件实例中使用。Ruby 2.2 引入了符号垃圾收集器,它可以释放动态生成的符号,因此动态创建符号产生的内存泄漏不再是问题。

回答你的问题:

如果我的应用程序或脚本中至少有两个相同的字符串,我是否必须使用符号而不是字符串?

如果您正在寻找的是要在代码内部使用的标识符,那么您应该使用符号。如果要打印输出,则应该使用字符串,即使它出现多次,甚至在内存中分配两个不同的对象。

原因如下:

  1. 打印符号将比打印字符串慢,因为它们被转换为字符串。
  2. 拥有许多不同的符号会增加应用程序的整体内存使用量,因为它们永远不会被释放。而且您永远不会同时使用代码中的所有字符串。

@AlanDert 的用例

@AlanDert:如果我在haml代码中多次使用 %input{type: :checkbox} 之类的东西,我应该使用什么作为复选框?

我可以。

@AlanDert:但是要在html页面上打印出一个符号,它应该被转换为字符串,不是吗?那么使用它有什么意义呢?

输入的类型是什么?您要使用的输入类型的标识符或要向用户显示的内容?

确实,它会在某个时候变成 HTML 代码,但是在您编写该行代码的那一刻,它意味着成为一个标识符 - 它标识您需要什么样的输入字段。因此,它在您的代码中反复使用,并且始终具有与标识符相同的“字符串”字符,并且不会产生内存泄漏。

也就是说,我们为什么不评估数据以查看字符串是否更快?

这是我为此创建的一个简单基准:

require 'benchmark'
require 'haml'

str = Benchmark.measure do
  10_000.times do
    Haml::Engine.new('%input{type: "checkbox"}').render
  end
end.total

sym = Benchmark.measure do
  10_000.times do
    Haml::Engine.new('%input{type: :checkbox}').render
  end
end.total

puts "String: " + str.to_s
puts "Symbol: " + sym.to_s

三个输出:

# first time
String: 5.14
Symbol: 5.07
#second
String: 5.29
Symbol: 5.050000000000001
#third
String: 4.7700000000000005
Symbol: 4.68

所以使用 smbols 实际上比使用字符串要快一点。这是为什么?这取决于 HAML 的实现方式。我需要对 HAML 代码进行一些修改才能看到,但是如果您继续在标识符的概念中使用符号,您的应用程序将更快更可靠。当问题出现时,对其进行基准测试并获得答案。

于 2013-05-18T05:38:52.703 回答
14

简而言之,符号是一个名称,由字符组成,但不可变。相反,字符串是字符的有序容器,其内容可以更改。

于 2013-05-18T07:17:14.373 回答
5

这是我在 codecademy 找到的一个不错的字符串 vs 符号基准:

require 'benchmark'

string_AZ = Hash[("a".."z").to_a.zip((1..26).to_a)]
symbol_AZ = Hash[(:a..:z).to_a.zip((1..26).to_a)]

string_time = Benchmark.realtime do
  1000_000.times { string_AZ["r"] }
end

symbol_time = Benchmark.realtime do
  1000_000.times { symbol_AZ[:r] }
end

puts "String time: #{string_time} seconds."
puts "Symbol time: #{symbol_time} seconds."

输出是:

String time: 0.21983 seconds.
Symbol time: 0.087873 seconds.
于 2015-06-12T10:06:18.903 回答
0
  • 使用符号作为哈希键标识符

    {key: "value"}

  • 符号允许您以不同的顺序调用方法

     def write(文件:,数据:,模式:“ascii”)
          # 为简洁起见删除
     结尾
     写(数据:123,文件:“test.txt”)
  • 冻结以保留为字符串并节省内存

    label = 'My Label'.freeze

于 2018-12-23T06:32:43.057 回答