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 elementInitialize และ 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 directlywithUnsafePointer สำหรับตัวแปรภายใน
หากต้องการได้ตัวชี้ชั่วคราวไปยังค่าที่มีอยู่โดยไม่ต้องจัดสรรหน่วยความจำ ให้ใช้ 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 behaviorstack แบบทำเองขนาดเล็ก
เมื่อนำทุกอย่างมารวมกัน คุณจะจัดสรรบล็อกหนึ่งบล็อก ใช้งานบล็อกนั้นเป็นองค์ประกอบด้วยการสร้างดัชนี แล้วทำความสะอาดในภายหลัง นี่คือพื้นฐานของคอลเลกชันแบบกำหนดเองและการเชื่อมต่อกับ 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- MemoryLayout และการจัดแนว
- UnsafePointer และ UnsafeMutablePointer
- ตัวชี้บัฟเฟอร์ที่ไม่ปลอดภัย
- withUnsafeBytes และการทำงานร่วมกับ C