object ทุก object ใน ruby สามารถมี instance variable ของตัวเองได้
2.instance_variable_set '@x', 1
2.instance_variable_get '@x' 4.instance_variable_get '@x' (4 - 2).instance_variable_get '@x'
บรรทัดที่น่าสนใจคือ
(4 - 2).instance_vairable_get '@x'
ถ้าจิตนาการ การทำงานภายในของ interpreter ณ บรรทัดนั้น ดู
- ขั้นแรกก็ต้อง new instance fixnum ค่าเป็น 4
- จากนั้นก็ new instance fixnum ค่าเป็น 2
- เรียกใช้ method
-
ของ instance 4 โดยมี parameter เป็น instance 2
- เรียกใช้ method instance_variable_get บน instance fixnum 4
ปัญหาเกิดที่ขั้นที่ 3 ว่ามันเรียก instance_variable_get บน object คนละตัวกับ object 2
แล้วทำไมค่ามันยังออกมาถูก มันมีกลไกพิเศษอะไรตรงไหน
ลองเปลี่ยนจาก fixnum เป็น String ดูบ้าง
"hello".instance_variable_set '@x', 1
puts "hello".instance_variable_get '@x' x = "hello"
x.instance_variable_set '@x', 1
puts x.instance_variable_get '@x'
จะเห็นได้ว่า instance_variable ของ 2 กับ "hello" มีพฤติกรรมไม่เหมือนกัน
ข่าวดี ไม่ใช่ทุกอย่างใน ruby ที่เป็น object อย่างเป็นทางการ
คำว่า object อย่างเป็นทางการ ก็คือ มี structure เป็นเรื่องเป็นราว เช่นมี id, methods, attributes
ถ้าจะให้ยกตัวอย่างชัดๆ ต้องดูที่ implementation ของ ruby ที่ใช้ c เขียน
struct RObject {
struct RBasic basic;
struct st_table *iv_tbl;
};
แต่ก็มีพวก special object ที่ไม่ได้ใช้ struct ในการ represent
object ต่อไปนี้ ใช้ unsigned long ตัวเดียวในการ implement object (แทนที่จะใช้ struct)
- small integers
- symbols
- true
- false
- nil
- Qundef (อันนี้ไม่รู้ว่ามันคืออะไร)
เหตุผลที่ต้อง implement special object แบบนี้ ก็คือเรื่อง performance
(เหมือนกับ java ที่ต้องมี primitive datatype)
ที่นี้ ruby สามารถให้ค่า instance_variable ที่ถูกต้อง ในกรณี fixnum ได้อย่างไร
ที่เป็นเช่นนี้ ก็เพราะวิธีการ implement ของ ruby เป็นแบบนี้
/* file variable.c */
static VALUE
ivar_get(obj, id, warn)
VALUE obj;
ID id;
int warn;
{
VALUE val;
switch (TYPE(obj)) {
case T_OBJECT:
case T_CLASS:
case T_MODULE:
if (ROBJECT(obj)->iv_tbl && st_lookup(ROBJECT(obj)->iv_tbl, id, &val))
return val;
break;
default:
if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj))
return generic_ivar_get(obj, id, warn);
break;
}
if (warn) {
rb_warning("instance variable %s not initialized", rb_id2name(id));
}
return Qnil;
}
จะเห็นว่ากรณีของพวก special object จะมีการใช้
generic_ivar_get
ซึ่ง function
generic_ivar_get
นี้จะไปค้นหาค่าจาก global hashtable ที่ share กันใช้ ระหว่าง special object
นี่เป็นสาเหตุให้ fixnum 2 สามารถเรียกใ้ช้ instace_variable_get แล้ว ยังได้ค่าเดิมกลับมา
ส่วนกรณี String, จริงๆแล้ว มันตก case เดียวกับ fixnum
แต่วิธีการ implement ของ String เป็นอีกแบบหนึ่ง
ลองดูตัวอย่างนี้
irb(main):001:0> 2.object_id
5
irb(main):002:0> (4-2).object_id
5
irb(main):003:0> "hello".object_id
175214
irb(main):004:0> "hello".object_id
169884
จะเห็นได้ว่า id ของ string มันเปลี่ยนไปทุกครั้ง
ดังนั้นการ lookup instance_variable ใน generic table ที่ต้องใช้ object_id เป็นตัวเปิด hashtable
ก็เลยไม่ได้ค่าเดิมออกมา