Objective-C และแนวคิดเรื่อง Memory Lifetime
ต้นแบบความคิดบางส่วนของ Rust?
หนึ่งในภาษาที่ผม “แอบเดา” ว่าอาจมีอิทธิพลต่อแนวคิดเรื่อง Memory Lifetime ของ Rust อยู่บ้าง
คือภาษา Objective-C
Objective-C เป็นภาษาที่ ไม่มี Garbage Collector (GC)
แต่ใช้ระบบ reference counting แทน
Reference Counting ใน Objective-C
ใน Objective-C การจัดการหน่วยความจำใช้แนวคิด
retain / release
หลักการง่ายมาก
retain→ เพิ่ม reference countrelease→ ลด reference count
ตัวอย่างเชิงแนวคิด
retain → +1
retain → +1
release → -1
release → -1
เมื่อ reference count กลายเป็น
0
object นั้นจะถูกทำลายทันที
Syntax ของ Objective-C
Syntax จะมีลักษณะเฉพาะแบบนี้
[a retain]
จริง ๆ แล้วนี่คือการ เรียก method
[a retain] → call method retain ของ object a
การใช้ retain / release ตาม Scope
ใน Objective-C สมัยก่อน เรามักถูกสอนให้ใช้ retain/release ให้ จับคู่กันตาม scope
ตัวอย่างเชิงแนวคิด
{
[a retain] // ref +1 == 1
...
{
[a retain] // ref +1 == 2
// ทำงานบางอย่าง
[a release] // ref -1 == 1
}
[a release] // ref -1 == 0 → a ถูกทำลายตรงนี้
}
แนวคิดคือ
retain → ใช้ → release
และพยายามให้มัน สมดุลกัน
ยุคของ ARC (Automatic Reference Counting)
ต่อมา Objective-C ได้เสนอระบบที่ทำสิ่งนี้ ให้อัตโนมัติ
สิ่งนี้เรียกว่า
ARC (Automatic Reference Counting)
หลักการคือ
compiler จะเป็นผู้แทรก
retain/releaseให้อัตโนมัติ
compiler จะวิเคราะห์โค้ด แล้วตัดสินว่า
- จุดไหนควร
retain - จุดไหนควร
release
คุ้น ๆ ไหม?
สิ่งนี้มีความคล้ายกับ Rust ในแง่หนึ่ง
compiler ไม่ได้ทำหน้าที่เพียง
translate code
แต่มันพยายาม
- วิเคราะห์โค้ด
- เข้าใจการใช้งาน
- ช่วยจัดการ resource
พูดอีกแบบคือ
compiler พยายาม “ฉลาดขึ้น”
ซึ่งเป็นแนวทางเดียวกับที่ Rust เลือกใช้
แต่ Rust ไปไกลกว่า
ความแตกต่างสำคัญคือ
Objective-C ยังยอมให้เกิดสถานการณ์แบบนี้ได้
retain หลายครั้ง
release น้อยครั้ง
เช่น
retain → +1
retain → +1
release → -1
ผลคือ reference count ไม่เคยเป็น 0
object จะ ค้างอยู่ในระบบตลอดไป
สิ่งนี้คือ
memory leak
ระบบ ARC ช่วยลดปัญหานี้ได้มาก แต่ยังคงพึ่งพา
วินัยของ programmer
เป็นหลัก
Rust เลือกแนวทางที่เข้มงวดกว่า
Rust ไม่ยอมให้
ownership ซ้ำ
โดยไม่มีเงื่อนไข
Rustใช้แนวคิด
ownership
borrowing
lifetime
และให้ compiler พิสูจน์ความถูกต้อง
ตั้งแต่ตอน
compile time
ก่อนที่โปรแกรมจะรัน
ลองคิดเล่น ๆ
ถ้า Objective-C ไม่อนุญาตให้
object เดียว
มี owner มากกว่า 1
มันจะเริ่มมีพฤติกรรมที่ คล้าย Rust มากขึ้นทันที
แน่นอนว่าโลกจริงซับซ้อนกว่านั้น แต่แนวคิดพื้นฐานจะเริ่มใกล้กัน
ทำไมการเรียน Memory Model หลายภาษา ถึงสำคัญ
ผมอยากแนะนำให้ลองศึกษาวิธีจัดการ memory ของหลายภาษา
คุณจะเริ่มเห็นว่า แต่ละภาษาแก้ปัญหาเดียวกัน แต่ใช้ แนวทางที่ต่างกัน
ตัวอย่างเช่น
| ภาษา | แนวคิด |
|---|---|
| C / C++ | manual memory |
| Objective-C / Swift | reference counting |
| Java / Go | garbage collector |
| Rust | ownership + lifetime |
ทำไมบางคนเข้าใจ Rust ได้เร็ว
คนที่เข้าใจ Rust lifetime ได้เร็ว มักจะเป็นคนที่เคยผ่านอย่างน้อยหนึ่งในนี้
- C / C++ → เคยเจอ dangling pointer
- Swift / Objective-C → เคยเจอ retain cycle / ARC
- Java / Go → เข้าใจ GC
- หรือเคย debug memory bug จริง ๆ
คนกลุ่มนี้จะมี
mental model ของ memory
อยู่แล้ว
พอเห็น Rust พวกเขาจะคิดทันทีว่า
อ๋อ Rust กำลังพยายามพิสูจน์สิ่งนี้ตั้งแต่ compile time
แต่สำหรับคนที่ไม่เคยผ่านสิ่งเหล่านี้
Lifetime ใน Rust จะดูเหมือน
กฎแปลก ๆ ของภาษา
เพราะมันบังคับให้ programmer ต้องเข้าใจ
memory model
ตั้งแต่แรก
Objective-C เป็นหนึ่งในภาษาที่ทำให้เราเห็นว่า
compiler สามารถช่วยจัดการ memory ได้
แต่ Rust ก้าวไปอีกขั้น
Rustไม่ได้เพียงแค่ช่วยจัดการ memory
Rustทำให้
ความผิดพลาดของ memory ไม่สามารถผ่านการ compile ได้ตั้งแต่แรก