+1

How Are Symbols And Strings Different?

Bạn đã bao giờ tự hỏi về sự khác nhau giữa symbols bà strings?

Trong bài viết này mình sẽ nói về điều này.

String được sử dụng để làm việc với data.

Còn Symbols dùng để định danh.

Khi nào thì sử dụng Symbols

Một trong những cách sử dụng phổ biến nhất của symbols là đại diện cho các method và instance variable names.

Ví dụ:

attr_reader :title

:title sau attr_reader là 1 symbol đại diện cho biến instance @title.

Bạn cũng có thể sử dụng symbols như hash keys.

Ví dụ:

hash = { a: 1, b: 2, c: 3 }

Lợi ích?

Symbols trông có vẻ đẹp hơn, nó bất biến (không thay đổi) và nếu bạn so sánh string keys và symbols keys bạn sã thấy rằng string keys chậm hơn khoảng 1.70x.

[1,2,3].send(:first)

Như vậy, bạn nên sử dụng symbols như tên hoặc methods và sử dụng strings khi bạn quan tâm nhiều hơn về dữ liệu (individual characters).

Chuyển đổi giữa Strings & Symbols

Trong một vài trường hợp, bạn có thể nhận giá trị trả về của 1 method là 1 symbol và bạn sẽ cần phải convert nó thành 1 string để có thể so sánh nó với string khác hoặc thực hiện các toán tử của string.

Bạn có thể sử dụng phương thức to_s.

Ví dụ, khi bạn sử dụng method_missing bạn sẽ nhận được tên của missing method dưới dạng một symbol và ban có thể muốn kiểm tra xem tên phương thức này có khớp với một mẫu nhất định (như kết thức bằng '?').

Ví dụ:

def method_missing(method_name, *args, &block)
  if method_name.to_s[-1] == "?"
    # do something
  else
    super
  end
end

Bạn cũng có thế chuyển đổi 1 string thành 1 symbol bằng cách sử dụng method to_sym.

"rubyguides".to_sym
:rybyguides

Tạo 1 mảng Symbols

Nếu bạn muốn tạo một mảng các symbols bạn có thế sử dụng:

symbols = %i(a b c)

[:a, :b, :c]

Tương tự đối với string ta sử dụng %w:

strings = %w(a b c)

["a", "b", "c"]

Symbol GC

Một sự thật thú vị khác về symbol đó là nó có nhiều loại khác nhau và lý do cho điều đó là symbol không được xử lý, loại bỏ (các symbols tạm thời) đối với các phiên bản trước Ruby 2.2, điều đó nghĩa là chúng không được làm sạch khỏi bộ nhớ (không loại bỏ khỏi bộ nhớ) khi chúng không còn cần sử dụng nữa giống như các đối tượng Ruby thông thường (string, hash, array...)

Ví dụ:

p Symbol.all_symbols.size
# 2443
 
('aa'..'aj').map(&:to_sym)
 
GC.start
p Symbol.all_symbols.size
# 2453

Bạn sẽ thấy rằng tổng số symbols tăng lên 10, giống như bạn mong đợi kể từ khi tạo ra 10 symbols mới bởi GC.start.

Nhưng kể từ khi Ruby 2.2 các symbols này được loại bỏ khỏi bộ nhớ bởi chúng chỉ là tạm thời.

Nếu bạn thử chạy đoạn code trên với phiên bản Ruby có GC thì kết quả của cả 2 symbol counts sẽ giống nhau.

Nhưng cũng có trường hợp đặc biệt, một số kí hiệu sẽ không bao giờ được xóa khỏi bộ nhớ chúng được gọi là "immortal symbols".

Bạn có thể đếm chúng bằng cách sử dụng ObjectSpace module:

require 'objspace'
 
ObjectSpace.count_symbols
 
{
  :mortal_dynamic_symbol=>3,
  :immortal_dynamic_symbol=>5,
  :immortal_static_symbol=>3663,
  :immortal_symbol=>3668
}

Lưu ý rằng symbols được tạo trực tiếp như :al sẽ tự động trở thành immortal symbols. Tạo ra một phương pháp mới cũng sẽ tạo một immortal_static_symbol để đi kèm với nó.

Vậy các mortal symbols có từ đâu?

Nó được có từ strings được chuyển thành các symbols bằng phương pháp to_sym.

Bạn có thể kiểm tra điều này bằng cách sử dụng:

ObjectSpace.count_symbols.

Và nếu bạn đang tự hỏi immortal_dynamic_symbol là gì, đó là một symbol đã được chuyển từ mortal thành immortal. Điều này có thể xảy ra khi bạn tạo một 1 method với tên của một mortal symbol.

Tổng kết

Điều bạn học được từ bài viết:

  • Symbols là không thay đổi.
  • Symbols không phải là con trỏ tới giá trị, nó là các giá trị.
  • Strings sử dụng cho data và symbols để nhận dạng.
  • Làm thế nào để chuyển đổi giữa strings và symbols.
  • Symbol GC đã được giới thiệu trong Ruby 2.2 để xóa các ký hiệu tạm thời.

Hy vọng bài viết sẽ giúp ích cho bạn.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.