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

MemoryLayout และการจัดแนว

ตรวจสอบขนาด ระยะก้าว และการจัดแนวของชนิดต่าง ๆ

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

เหตุใดโครงสร้างหน่วยความจำจึงสำคัญ

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

สิ่งนี้ตอบคำถามสามข้อ ได้แก่ ค่าหนึ่งค่ามีขนาดเท่าใด ค่าในอาร์เรย์อยู่ห่างกันเท่าใด และค่าต้องเริ่มต้นบนขอบเขตที่อยู่แบบใด

// MemoryLayout works on any type
print(MemoryLayout<Int>.size)       // 8 on 64-bit
print(MemoryLayout<Bool>.size)      // 1
print(MemoryLayout<Double>.size)    // 8

size — พื้นที่จัดเก็บจริง

MemoryLayout<T>.size คือจำนวนไบต์ที่ข้อมูลสำคัญของค่าหนึ่งค่าใช้พื้นที่ โดยไม่รวมส่วนเติมเต็มท้ายที่อาจอยู่ระหว่างสมาชิกในอาร์เรย์

ใช้ size เมื่อคุณต้องการทราบว่าค่าหนึ่งค่ามีไบต์ที่มีความหมายอยู่กี่ไบต์

struct Point {
    var x: Int8   // 1 byte
    var y: Int64  // 8 bytes
}

print(MemoryLayout<Point>.size)   // 16 (not 9 — padding!)

stride — การเว้นระยะในอาร์เรย์

stride คือระยะห่างเป็นไบต์ระหว่างจุดเริ่มต้นของสมาชิกหนึ่งกับสมาชิกถัดไป เมื่อค่าต่าง ๆ ถูกจัดเรียงในอาร์เรย์ต่อเนื่อง ระยะนี้เท่ากับ size ที่ปัดขึ้นตามค่า alignment

เมื่อคุณจัดสรรบัฟเฟอร์ด้วยตนเอง ให้คูณด้วย stride และอย่าคูณด้วย size

print(MemoryLayout<Point>.stride) // 16
print(MemoryLayout<Int8>.stride)  // 1

// Buffer for 10 Points needs:
let bytes = 10 * MemoryLayout<Point>.stride // 160

alignment — ขอบเขตของที่อยู่

alignment คือขอบเขตของไบต์ที่ค่าต้องเริ่มต้น ค่าแบบจำนวนเต็ม 64 บิตที่มี alignment เท่ากับ 8 ต้องเริ่มต้นที่ที่อยู่ซึ่งหารด้วย 8 ลงตัว คอมไพเลอร์จะใส่ส่วนเติมเต็มเพื่อให้เป็นไปตามเงื่อนไขนี้

alignment ของโครงสร้างคือค่า alignment ที่มากที่สุดในบรรดาสมาชิกของโครงสร้าง

print(MemoryLayout<Int8>.alignment)   // 1
print(MemoryLayout<Int64>.alignment)  // 8
print(MemoryLayout<Point>.alignment)  // 8

ความแตกต่างระหว่าง size กับ stride

สำหรับชนิดข้อมูลจำนวนมาก size == stride แต่ทั้งสองค่าจะแตกต่างกันเมื่อมีส่วนเติมเต็ม โครงสร้างหนึ่งอาจมี size เท่ากับ 9 แต่มี stride เท่ากับ 16 เพราะสมาชิกถัดไปต้องจัดแนวให้ตรงกับขอบเขต 8 ไบต์

หลักจำง่าย: ใช้ size เพื่อพิจารณาค่าเดียว และใช้ stride เพื่อพิจารณากลุ่มข้อมูล

struct Mixed {
    var flag: Bool   // 1
    var value: Int   // 8
}
print(MemoryLayout<Mixed>.size)   // 16
print(MemoryLayout<Mixed>.stride) // 16

ออฟเซ็ตของฟิลด์

สำหรับโครงสร้าง คุณสามารถตรวจสอบได้ว่าคุณสมบัติที่จัดเก็บไว้แต่ละรายการอยู่ห่างจากจุดเริ่มต้นเท่าใด โดยใช้ MemoryLayout<T>.offset(of:) ร่วมกับเส้นทางคีย์ วิธีนี้จำเป็นเมื่อต้องจัดวางข้อมูลให้ตรงกับโครงสร้างข้อมูลของ C

struct Header {
    var magic: UInt32  // offset 0
    var length: UInt64 // offset 8 (padded)
}

print(MemoryLayout<Header>.offset(of: \Header.magic) ?? -1)  // 0
print(MemoryLayout<Header>.offset(of: \Header.length) ?? -1) // 8

การจัดลำดับใหม่เพื่อประหยัดพื้นที่

เนื่องจาก alignment ลำดับของคุณสมบัติที่จัดเก็บไว้จึงมีผลต่อ size การวางฟิลด์ขนาดใหญ่ไว้ก่อนหรือจัดกลุ่มตามขนาดอาจช่วยลดขนาดโครงสร้างได้ เรื่องนี้สำคัญสำหรับอาร์เรย์ขนาดใหญ่ เพราะไบต์ที่สูญเปล่าจะสะสมเพิ่มขึ้น

struct Wasteful { var a: Int8; var b: Int64; var c: Int8 } // size 24
struct Tight    { var b: Int64; var a: Int8; var c: Int8 } // size 16

print(MemoryLayout<Wasteful>.size)
print(MemoryLayout<Tight>.size)

โครงสร้างของการอ้างอิง

อินสแตนซ์ของคลาสถูกจัดสรรบนฮีพ ดังนั้นตัวแปรที่เก็บการอ้างอิงไปยังคลาสจึงเป็นเพียงตัวชี้ ค่า MemoryLayout.size ของตัวแปรนี้มีขนาดเท่ากับตัวชี้ หรือ 8 ไบต์ ไม่ว่าออบเจ็กต์จะมีขนาดเท่าใด

ชนิดค่าอย่างโครงสร้างจะเก็บเนื้อหาไว้ภายในตัวมันเอง ส่วนชนิดอ้างอิงจะเก็บไว้เพียงตัวชี้

class Big { var data = [Int](repeating: 0, count: 1000) }

print(MemoryLayout<Big>.size) // 8 — just the reference

ออปชันนัลและอีนัม

ออปชันนัลมักเพิ่มหนึ่งไบต์สำหรับแท็ก แต่สวิฟต์จะปรับออปชันนัลของการอ้างอิงให้ใช้ตัวชี้ว่างร่วมกัน จึงทำให้ชนิดสตริงแบบออปชันนัลอาจมีขนาดเท่ากับชนิดสตริงปกติ

อีนัมที่มีค่าที่เชื่อมโยงจะมีขนาดพอสำหรับกรณีที่ใหญ่ที่สุด รวมกับตัวจำแนก

print(MemoryLayout<Int?>.size)    // 9
print(MemoryLayout<String>.size)  // 16
print(MemoryLayout<String?>.size) // 16 — no extra byte

การใช้รูปแบบอินสแตนซ์ของ:

สมาชิกทุกตัว ได้แก่ size, stride และ alignment ต่างก็มีรูปแบบที่อิงจากอินสแตนซ์ เช่น MemoryLayout.size(ofValue:) รูปแบบนี้มีประโยชน์เมื่อเขียนระบุชนิดได้ยาก หรือเมื่อชนิดถูกอนุมานโดยอัตโนมัติ

let tuple = (UInt8(1), UInt64(2))
print(MemoryLayout.size(ofValue: tuple))      // 16
print(MemoryLayout.stride(ofValue: tuple))    // 16
print(MemoryLayout.alignment(ofValue: tuple)) // 8

การจัดสรรด้วยโครงสร้างที่ถูกต้อง

เมื่อคุณ allocate หน่วยความจำ raw คุณต้องส่งทั้งจำนวนไบต์และค่า alignment การดึงค่าเหล่านี้จาก MemoryLayout ช่วยรับรองความถูกต้องในสถาปัตยกรรมต่าง ๆ แทนการกำหนดตัวเลขตายตัว

let count = 4
let capacity = count * MemoryLayout<Int>.stride
let align = MemoryLayout<Int>.alignment

let raw = UnsafeMutableRawPointer.allocate(
    byteCount: capacity, alignment: align)
defer { raw.deallocate() }

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

ทดสอบความเข้าใจเกี่ยวกับความแตกต่างระหว่าง size กับ stride

สรุปทบทวน

คุณได้เรียนรู้หลักสามประการของโครงสร้างหน่วยความจำ:

  • size — ไบต์ที่มีความหมายของค่าหนึ่งค่า
  • stride — ระยะห่างระหว่างสมาชิกในอาร์เรย์ ใช้ค่านี้เมื่อต้องจัดสรรหน่วยความจำ
  • alignment — ขอบเขตเริ่มต้นที่จำเป็น

คุณยังได้เห็น offset(of:) สำหรับระบุตำแหน่งของฟิลด์ ได้เรียนรู้ว่าลำดับของคุณสมบัติมีผลต่อ size และเห็นว่าการอ้างอิงมีขนาดเท่ากับตัวชี้เท่านั้น เครื่องมือเหล่านี้ช่วยให้คุณจัดสรรและแปลความหมายหน่วยความจำ raw ได้อย่างปลอดภัย

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

บทเรียน “MemoryLayout และการจัดแนว” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “MemoryLayout และการจัดแนว”

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

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

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

บทเรียน “MemoryLayout และการจัดแนว” ใช้เวลานานแค่ไหน

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

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

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

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

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