0Pricing
Swift Academy · บทเรียน

UnsafePointer และ UnsafeMutablePointer

อ่านและเขียนหน่วยความจำดิบอย่างระมัดระวัง

UnsafePointer และ UnsafeMutablePointer เป็นบทเรียน Swift Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Swift Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน

ตัวชี้ที่ระบุชนิดในสวิฟต์

การอ้างอิงที่ปลอดภัยของสวิฟต์จะไม่เปิดเผยที่อยู่ raw แต่เมื่อเชื่อมต่อกับ C หรือสร้างโครงสร้างข้อมูลระดับล่าง คุณสามารถใช้ตัวชี้ที่ระบุชนิดได้ ได้แก่ UnsafePointer<T> สำหรับการเข้าถึงแบบอ่านอย่างเดียว และ UnsafeMutablePointer<T> สำหรับการอ่านและเขียน

ตัวชี้ที่ระบุชนิดจะรู้ชนิดขององค์ประกอบ จึงสามารถคำนวณ stride และอ่านค่าโดยใช้ชนิดที่ถูกต้องได้

// A pointer to an Int we can read but not write
func readFirst(_ p: UnsafePointer<Int>) -> Int {
    return p.pointee
}

คุณสมบัติค่าที่ตัวชี้ชี้อยู่

pointee คือค่าที่ตัวชี้กำลังชี้ไป ในตัวชี้ที่แก้ไขได้ คุณสามารถอ่านและกำหนดค่าได้ ส่วนตัวชี้แบบอ่านอย่างเดียวจะอ่านได้เท่านั้น

การเข้าถึง pointee ในหน่วยความจำที่ยังไม่ได้ initialize หรือถูก deallocate แล้ว ถือเป็นพฤติกรรมที่ไม่ได้กำหนดไว้ ระบบชนิดข้อมูลจะตรวจจับกรณีนี้ไม่ได้

let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
p.initialize(to: 42)
print(p.pointee)   // 42
p.pointee = 99
print(p.pointee)   // 99

การจัดสรรหน่วยความจำ

UnsafeMutablePointer<T>.allocate(capacity:) จะจองพื้นที่สำหรับองค์ประกอบจำนวน capacity รายการ แต่จะไม่ initialize องค์ประกอบเหล่านั้น หน่วยความจำจะอยู่ในรูป raw และการอ่านก่อน initialize ถือว่าผิดกฎ

// Reserve space for 3 Doubles (uninitialized)
let buffer = UnsafeMutablePointer<Double>.allocate(capacity: 3)
// Must initialize before reading any element

Initialize และ Deinitialize

ก่อนอ่านค่าคุณต้อง initialize ก่อน และก่อน deallocate ชนิดข้อมูลที่ต้องทำความสะอาด เช่น คลาสหรือสตริง คุณต้อง deinitialize เพื่อปล่อยทรัพยากรของชนิดข้อมูลนั้น

วงจรการทำงานคือ: allocate → initialize → ใช้งาน → deinitialize → deallocate

let p = UnsafeMutablePointer<String>.allocate(capacity: 1)
p.initialize(to: "hello")
print(p.pointee)
p.deinitialize(count: 1) // releases the String
p.deallocate()

จับคู่ allocate กับ deallocate เสมอ

ทุก allocate ต้องจับคู่กับ deallocate เพียงครั้งเดียว ไม่เช่นนั้นจะเกิดหน่วยความจำรั่ว การใช้คำสั่งเลื่อนการทำงานทันทีหลังการจัดสรรเป็นรูปแบบที่ปลอดภัยที่สุด เพราะคำสั่งนี้จะทำงานไม่ว่าฟังก์ชันจะสิ้นสุดด้วยวิธีใด

func process() {
    let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
    defer { p.deallocate() }
    p.initialize(to: 7)
    print(p.pointee)
} // deallocate runs here automatically

การคำนวณด้วยตัวชี้

ตัวชี้ที่ระบุชนิดรองรับการสร้างดัชนีและการคำนวณโดยใช้หน่วยขององค์ประกอบ ไม่ใช่ไบต์ p + 2 จะเลื่อนไปข้างหน้าสอง stride ของ T ส่วน p[i] เป็นรูปย่อของ (p + i).pointee

let p = UnsafeMutablePointer<Int>.allocate(capacity: 3)
p.initialize(repeating: 0, count: 3)
p[0] = 10
p[1] = 20
p[2] = 30
print((p + 1).pointee) // 20
p.deinitialize(count: 3)
p.deallocate()

Initialize หลายองค์ประกอบ

สำหรับบล็อกหน่วยความจำ initialize(repeating:count:) จะกำหนดค่าเดียวกันให้ทุกช่อง ส่วน initialize(from:count:) จะคัดลอกจากบัฟเฟอร์อื่น ทั้งสองแบบ initialize ช่วงข้อมูลทั้งหมดอย่างสมบูรณ์ จึงสามารถอ่านได้อย่างปลอดภัย

let dst = UnsafeMutablePointer<Int>.allocate(capacity: 4)
dst.initialize(repeating: 5, count: 4)
print(dst[3]) // 5
dst.deinitialize(count: 4)
dst.deallocate()

แบบอ่านอย่างเดียวกับแบบแก้ไขได้

ฟังก์ชันที่ตรวจสอบข้อมูลเท่านั้นควรรับ UnsafePointer<T> ส่วนฟังก์ชันที่แก้ไขข้อมูลควรรับ UnsafeMutablePointer<T> ตัวชี้ที่แก้ไขได้สามารถแปลงเป็นตัวชี้แบบอ่านอย่างเดียวได้โดยอัตโนมัติ แต่แปลงย้อนกลับไม่ได้ ซึ่งช่วยบังคับเจตนาที่ขอบเขตของเอพีไอ

func sum(_ p: UnsafePointer<Int>, _ n: Int) -> Int {
    var total = 0
    for i in 0..<n { total += p[i] }
    return total
}
// A mutable pointer can be passed here directly

withUnsafePointer สำหรับตัวแปรภายใน

หากต้องการได้ตัวชี้ชั่วคราวไปยังค่าที่มีอยู่โดยไม่ต้องจัดสรรหน่วยความจำ ให้ใช้ withUnsafePointer(to:) หรือรูปแบบที่แก้ไขได้ของมัน ตัวชี้นี้ใช้ได้เฉพาะภายในคลอสเชอร์เท่านั้น ห้ามปล่อยให้ตัวชี้หลุดออกไป

var value = 100
withUnsafeMutablePointer(to: &value) { ptr in
    ptr.pointee += 1
}
print(value) // 101

ข้อผิดพลาดที่พบบ่อย

ตัวชี้ที่ระบุชนิดมีประสิทธิภาพสูง แต่ไม่ผ่อนปรนต่อการใช้งานที่ผิดพลาด:

  • การอ่าน pointee ก่อน initialize ถือเป็นพฤติกรรมที่ไม่ได้กำหนดไว้
  • การลืม deinitialize กับชนิดข้อมูลที่ถือทรัพยากรจะทำให้เกิดการรั่วไหล
  • การปล่อยให้ตัวชี้จาก withUnsafePointer หลุดออกไปจะทำให้เกิดตัวชี้ห้อย

คอมไพเลอร์จะเชื่อว่าคุณจัดการถูกต้องในส่วนนี้ จึงไม่มีกลไกป้องกันขณะทำงาน

// DANGEROUS — pointer escapes the closure
// var escaped: UnsafePointer<Int>?
// withUnsafePointer(to: &x) { escaped = $0 }
// escaped!.pointee  // dangling, undefined behavior

stack แบบทำเองขนาดเล็ก

เมื่อนำทุกอย่างมารวมกัน คุณจะจัดสรรบล็อกหนึ่งบล็อก ใช้งานบล็อกนั้นเป็นองค์ประกอบด้วยการสร้างดัชนี แล้วทำความสะอาดในภายหลัง นี่คือพื้นฐานของคอลเลกชันแบบกำหนดเองและการเชื่อมต่อกับ C

let n = 3
let stack = UnsafeMutablePointer<Int>.allocate(capacity: n)
stack.initialize(repeating: 0, count: n)
for i in 0..<n { stack[i] = i * i }
// stack now holds 0, 1, 4
stack.deinitialize(count: n)
stack.deallocate()

ตรวจสอบความเข้าใจ

ทบทวนวงจรการทำงานของตัวชี้ที่ถูกต้อง

สรุปทบทวน

ตอนนี้คุณรู้จักตัวชี้ที่ระบุชนิดแล้ว:

  • UnsafePointer ใช้อ่านค่า ส่วน UnsafeMutablePointer ใช้อ่านและเขียนค่า
  • pointee ใช้เข้าถึงค่า และการคำนวณใช้หน่วยขององค์ประกอบ
  • วงจรการทำงานคือ allocate → initialize → ใช้งาน → deinitialize → deallocate โดยควรมี defer คอยควบคุม

ต่อไปคุณจะได้เห็นว่าตัวชี้บัฟเฟอร์ห่อหุ้มพื้นที่ต่อเนื่องทั้งหมดไว้ในเอพีไอที่ใช้งานกับคอลเลกชันได้สะดวกอย่างไร

คำถามที่พบบ่อย

บทเรียน “UnsafePointer และ UnsafeMutablePointer” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “UnsafePointer และ UnsafeMutablePointer” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Swift Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “UnsafePointer และ UnsafeMutablePointer”

อ่านและเขียนหน่วยความจำดิบอย่างระมัดระวัง คุณปฏิบัติ Swift Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Swift Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Swift Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “UnsafePointer และ UnsafeMutablePointer” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Swift Academy นี้ได้ไหม

ได้ บทเรียน Swift Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. MemoryLayout และการจัดแนว
  2. UnsafePointer และ UnsafeMutablePointer
  3. ตัวชี้บัฟเฟอร์ที่ไม่ปลอดภัย
  4. withUnsafeBytes และการทำงานร่วมกับ C
← กลับไปที่ Swift Academy