withUnsafeBytes และการทำงานร่วมกับ C
เชื่อมต่อกับ API ของ C ที่ต้องการไบต์ดิบ
withUnsafeBytes และการทำงานร่วมกับ C เป็นบทเรียน Swift Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Swift Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน
จากชนิดข้อมูลสู่ไบต์ดิบ
บางครั้งคุณต้องมองค่าหนึ่งไม่ใช่ในรูปของ Int หรือ struct แต่เป็นลำดับของไบต์ดิบ เพื่อคำนวณแฮช เขียนลงไฟล์ หรือส่งให้ API ของ C withUnsafeBytes มอบสิ่งนั้นให้คุณโดยตรง นั่นคือมุมมองชั่วคราวของพื้นที่จัดเก็บค่าในรูปของ UInt8
var value: UInt32 = 0x01020304
withUnsafeBytes(of: &value) { rawBuffer in
print(rawBuffer.count) // 4 bytes
}UnsafeRawBufferPointer
คลอเชอร์จะได้รับ UnsafeRawBufferPointer ซึ่งเป็นบัฟเฟอร์ของไบต์ที่ไม่มีชนิดขององค์ประกอบ คุณใช้ดัชนีเพื่ออ่านค่า UInt8 แต่ละรายการได้ บัฟเฟอร์นี้อ่านได้อย่างเดียว ส่วนรูปแบบที่เปลี่ยนแปลงได้คือ withUnsafeMutableBytes
var value: UInt32 = 0x01020304
withUnsafeBytes(of: &value) { bytes in
for b in bytes { print(String(format: "%02x", b)) }
}
// Order depends on endiannessลำดับไบต์มีความสำคัญ
ลำดับไบต์ที่คุณสังเกตเห็นขึ้นอยู่กับลำดับไบต์ของแพลตฟอร์ม บนเครื่องแบบ little-endian (Apple silicon, Intel) ไบต์ที่มีนัยสำคัญน้อยที่สุดจะอยู่ก่อน เมื่อทำการจัดลำดับข้อมูลสำหรับโพรโทคอล ให้แปลงอย่างชัดเจนด้วย bigEndian หรือ littleEndian
let host: UInt32 = 0x01020304
let networkOrder = host.bigEndian
var be = networkOrder
withUnsafeBytes(of: &be) { bytes in
// Always 01 02 03 04 regardless of platform
print(Array(bytes))
}ไบต์ของ struct
withUnsafeBytes(of:) ทำงานกับค่าใด ๆ ก็ได้ รวมถึง struct ความยาวของบัฟเฟอร์เท่ากับ MemoryLayout<T>.size และไบต์เติมช่องว่างใด ๆ จะถูกรวมอยู่ด้วย แต่เนื้อหาของไบต์เหล่านั้นไม่ได้รับการระบุ
struct RGB { var r: UInt8; var g: UInt8; var b: UInt8 }
var color = RGB(r: 255, g: 128, b: 0)
withUnsafeBytes(of: &color) { bytes in
print(bytes[0], bytes[1], bytes[2]) // 255 128 0
}การโหลดค่าจากไบต์
หากต้องการตีความไบต์ดิบใหม่เป็นค่าแบบมีชนิด ให้ใช้ load(fromByteOffset:as:) กับบัฟเฟอร์ดิบ ออฟเซ็ตต้องสอดคล้องกับการจัดแนวของชนิดข้อมูล และไบต์นั้นต้องแทนค่าที่ถูกต้องของชนิดข้อมูลดังกล่าวจริง ๆ
let raw: [UInt8] = [0x04, 0x03, 0x02, 0x01]
let n = raw.withUnsafeBytes { ptr in
ptr.load(fromByteOffset: 0, as: UInt32.self)
}
print(String(format: "0x%08x", n))loadUnaligned สำหรับข้อมูลแบบแพ็ก
รูปแบบไบนารีมักจัดเก็บฟิลด์โดยไม่มีไบต์เติมเพื่อจัดแนว การเรียก load ที่ออฟเซ็ตซึ่งไม่ได้จัดแนวถือเป็นพฤติกรรมที่ไม่มีการกำหนด ให้ใช้ loadUnaligned(fromByteOffset:as:) แทน เพื่ออ่านจากออฟเซ็ตใด ๆ ได้อย่างปลอดภัย
let packet: [UInt8] = [0xFF, 0x04, 0x03, 0x02, 0x01]
let field = packet.withUnsafeBytes { ptr in
ptr.loadUnaligned(fromByteOffset: 1, as: UInt32.self)
}
print(field)การเชื่อมข้อมูล Swift เข้ากับ C
ฟังก์ชัน C จำนวนมากรับ const void * พร้อมความยาว Data และอาร์เรย์เปิดให้ใช้ withUnsafeBytes คุณจึงส่ง baseAddress และ count ได้โดยตรงโดยไม่ต้องคัดลอกข้อมูล
import Foundation
let data = Data([0x68, 0x69]) // "hi"
data.withUnsafeBytes { (raw: UnsafeRawBufferPointer) in
if let base = raw.baseAddress {
// c_consume(base, raw.count)
print("ptr", base, "len", raw.count)
}
}การรับตัวชี้จาก C
เมื่อคอลแบ็กของ C ส่ง const void * พร้อมความยาวมาให้คุณ ให้ห่อหุ้มข้อมูลนั้นใน UnsafeRawBufferPointer เพื่อวนซ้ำอย่างปลอดภัย Swift จะนำเข้าตัวชี้ C เป็น UnsafeRawPointer?
func handleC(_ ptr: UnsafeRawPointer, _ len: Int) {
let buffer = UnsafeRawBufferPointer(start: ptr, count: len)
let checksum = buffer.reduce(0) { $0 &+ Int($1) }
print("checksum:", checksum)
}bindMemory และการตีความใหม่
หากต้องการปฏิบัติต่อพื้นที่ดิบเสมือนเป็นองค์ประกอบที่มีชนิด bindMemory(to:capacity:) จะบอกคอมไพเลอร์ว่าควรตีความพื้นที่นั้นอย่างไร การใช้สิ่งนี้ผิดวิธี เช่น ผูกหน่วยความจำเดียวกันเข้ากับชนิดที่เข้ากันไม่ได้สองชนิด จะละเมิดกฎการใช้นามแฝงอย่างเข้มงวดของ Swift และถือเป็นพฤติกรรมที่ไม่มีการกำหนด
let raw = UnsafeMutableRawPointer.allocate(
byteCount: 8, alignment: 8)
defer { raw.deallocate() }
let typed = raw.bindMemory(to: Int.self, capacity: 1)
typed.pointee = 1234
print(typed.pointee)assumingMemoryBound เทียบกับ bindMemory
bindMemory จะเปลี่ยนวิธีตีความหน่วยความจำอย่างถาวร ส่วน assumingMemoryBound(to:) จะยืนยันว่าหน่วยความจำนั้น ผูกกับชนิดดังกล่าวอยู่แล้ว โดยไม่เปลี่ยนแปลงสิ่งใด ให้ใช้แบบหลังเฉพาะเมื่อคุณมั่นใจในการผูกที่มีอยู่ เช่น หน่วยความจำที่คุณทราบว่าจัดเก็บชนิดข้อมูลที่กำหนด
func sumInts(_ raw: UnsafeRawPointer, _ count: Int) -> Int {
let p = raw.assumingMemoryBound(to: Int.self)
var total = 0
for i in 0..<count { total += p[i] }
return total
}รายการตรวจสอบความปลอดภัย
การทำงานร่วมกันระหว่างไบต์ดิบกับ C เป็นจุดที่มักซ่อนสาเหตุของการหยุดทำงานไว้มากที่สุด โปรดจำไว้ว่า:
- อย่าให้ตัวชี้ของคลอเชอร์หลุดออกไปใช้นอกคลอเชอร์
- เคารพการจัดแนว หรือใช้รูปแบบ
Unaligned - จัดการ
endiannessอย่างชัดเจนเมื่อจัดลำดับข้อมูล - อย่าผูกหน่วยความจำใหม่เข้ากับชนิดที่ขัดแย้งกัน
- ตรวจสอบกรณีที่
baseAddressเป็นnilสำหรับบัฟเฟอร์ว่าง
// Convert any value to a hex string safely
import Foundation
func hex<T>(_ v: T) -> String {
var value = v
return withUnsafeBytes(of: &value) { bytes in
bytes.map { String(format: "%02x", $0) }.joined()
}
}ตรวจสอบความเข้าใจ
ทดสอบความเข้าใจของคุณเกี่ยวกับการอ่านข้อมูลไบนารีแบบแพ็ก
สรุป
คุณได้เรียนรู้การเชื่อมค่าของ Swift และ C ผ่านไบต์ดิบ:
withUnsafeBytes(of:)แสดงค่าใด ๆ ในรูปของUInt8load/loadUnalignedตีความไบต์ใหม่เป็นค่าแบบมีชนิด โดยคำนึงถึงการจัดแนวDataและอาร์เรย์เปิดให้ใช้withUnsafeBytesเพื่อส่งตัวชี้ + ความยาวให้ CbindMemoryและassumingMemoryBoundควบคุมวิธีระบุชนิดให้หน่วยความจำดิบ- ลำดับไบต์ การจัดแนว และการที่ตัวชี้หลุดออกไปใช้นอกขอบเขต คืออันตรายหลัก
คำถามที่พบบ่อย
บทเรียน “withUnsafeBytes และการทำงานร่วมกับ C” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “withUnsafeBytes และการทำงานร่วมกับ C” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Swift Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “withUnsafeBytes และการทำงานร่วมกับ C”
เชื่อมต่อกับ API ของ C ที่ต้องการไบต์ดิบ คุณปฏิบัติ Swift Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Swift Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Swift Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “withUnsafeBytes และการทำงานร่วมกับ C” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Swift Academy นี้ได้ไหม
ได้ บทเรียน Swift Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- MemoryLayout และการจัดแนว
- UnsafePointer และ UnsafeMutablePointer
- ตัวชี้บัฟเฟอร์ที่ไม่ปลอดภัย
- withUnsafeBytes และการทำงานร่วมกับ C