Sendable คือโปรโตคอล Swift ที่ทำเครื่องหมายประเภทที่ปลอดภัยสำหรับการส่งระหว่างเธรด เมื่อทำงานกับการทำงานพร้อมกัน (actor, async/await, Task) คอมไพเลอร์ Swift กำหนดให้ข้อมูลทั้งหมดที่ส่งระหว่างบริบทที่แยก isolated ต้องสอดคล้องกับ Sendable ซึ่งป้องกันการส่งประเภทที่ไม่ปลอดภัยโดยไม่ได้ตั้งใจที่อาจนำไปสู่สภาวะการแข่งขันของข้อมูล โปรโตคอลทำหน้าที่เป็นสัญญา: ประเภทที่สอดคล้องกับ Sendable รับประกันว่าไม่มีสถานะภายในที่ไม่ถูกซิงโครไนซ์ ตาม WWDC 2021 โปรโตคอล Sendable เป็นองค์ประกอบบังคับเมื่อออกแบบสถาปัตยกรรมแบบหลายเธรดที่ปลอดภัย
ประเด็นสำคัญ
Sendable คือโปรโตคอลจากไลบรารีมาตรฐาน Swift (SE-0302) ที่ทำเครื่องหมายประเภทที่ปลอดภัยสำหรับการส่งระหว่างโดเมนที่แยก isolated โดเมนที่แยก isolated หมายถึง actor, งาน Task หรือบริบท @MainActor Sendable ไม่มีข้อกำหนดใดๆ — มันทำหน้าที่เป็นมาร์กเกอร์ด้านความปลอดภัย
ก่อน Swift 5.5 นักพัฒนาสามารถส่งวัตถุใดๆ ระหว่างเธรดผ่าน DispatchQueue และคอมไพเลอร์ไม่ได้ตรวจสอบว่าปลอดภัยหรือไม่ Sendable ปิดช่องโหว่นี้: ตอนนี้คอมไพเลอร์จะติดตามการส่งข้ามเธรดด้วยตัวเองและบล็อกการส่งที่ไม่ปลอดภัย ซึ่งทำให้โค้ดที่ทำงานพร้อมกันมีความน่าเชื่อถือมากขึ้นแม้ก่อนที่จะทำงาน
struct UserProfile: Sendable {
let name: String
let age: Int
}
class NonSendableClass {
var counter: Int = 0
}
ในตัวอย่าง UserProfile สามารถส่งระหว่างเธรดได้อย่างปลอดภัยเพราะเป็น struct ที่มีคุณสมบัติคงที่ของประเภท Sendable NonSendableClass จะทำให้เกิดข้อผิดพลาดในการคอมไพล์เมื่อพยายามส่งผ่าน await หรือเข้าไปใน Task
ประเภท Sendable ใน Swift แบ่งออกเป็นสามหมวดหมู่: ประเภทค่าที่มีคุณสมบัติ Sendable, คลาส final ที่มีสถานะไม่เปลี่ยนแปลง และฟังก์ชัน/คลอเชอร์ที่ทำเครื่องหมายด้วย @Sendable คอมไพเลอร์จะอนุมานความสอดคล้องกับ Sendable สำหรับประเภทค่าโดยอัตโนมัติ
Struct, Enum และ tuple จะกลายเป็น Sendable โดยอัตโนมัติหากคุณสมบัติทั้งหมดและค่าที่เกี่ยวข้องของพวกมันเป็น Sendable เช่นกัน นี่เป็นแนวทางที่อนุรักษ์นิยม: หากอย่างน้อยหนึ่งคุณสมบัติไม่สอดคล้องกับ Sendable ประเภททั้งหมดจะไม่ได้รับการยอมรับว่าปลอดภัย
| ประเภท | Sendable | เงื่อนไข |
|---|---|---|
| Struct | ใช่ (ไม่ต้องประกาศอย่างชัดแจ้ง) | คุณสมบัติทั้งหมดเป็น Sendable |
| Enum | ใช่ (ไม่ต้องประกาศอย่างชัดแจ้ง) | ค่าที่เกี่ยวข้องทั้งหมดเป็น Sendable |
| Final class | ใช่ (ต้องประกาศอย่างชัดแจ้ง) | คุณสมบัติ let ทั้งหมดเป็น Sendable ไม่มี var |
| Non-final class | ไม่ | ไม่สามารถเป็น Sendable เนื่องจากการสืบทอด |
ประเภทในตัวทั้งหมดของ Swift — Int, String, Double, Bool, Optional, Array, Dictionary, Set — สอดคล้องกับ Sendable ซึ่งทำให้การประกอบประเภทปลอดภัยโดยค่าเริ่มต้น นักพัฒนาเพียงต้องระวังคลาสที่กำหนดเอง
@unchecked Sendable เป็นกลไกที่อนุญาตให้คลาสประกาศตัวเองอย่างชัดแจ้งว่าเป็น Sendable โดยข้ามการตรวจสอบของคอมไพเลอร์ นักพัฒนารับผิดชอบต่อความปลอดภัยของเธรดของคลาสดังกล่าว ซึ่งมีประโยชน์สำหรับบริดจ์ Objective-C และโครงสร้างที่ปรับให้เหมาะสม
@unchecked Sendable ถูกใช้เมื่อคลาสรับประกันความปลอดภัยภายในผ่านล็อกหรือการดำเนินการอะตอมมิก แต่คอมไพเลอร์ไม่สามารถตรวจสอบได้ในเชิงสถิต ตัวอย่างเช่น คลาสที่มี os_unfair_lock หรือ pthread_mutex_t — ความปลอดภัยของเธรดของมันถูกรับประกันโดยโค้ด แต่ Swift ไม่เห็นสิ่งนี้
final class AtomicCounter: @unchecked Sendable {
private var value: Int = 0
private let lock = NSLock()
func increment() {
lock.lock()
value += 1
lock.unlock()
}
}
การใช้ @unchecked Sendable ควรทำ ด้วยความระมัดระวัง มันเป็นสัญญาณที่ชัดเจนถึงนักพัฒนาคนอื่น: «ฉันตรวจสอบแล้ว มันปลอดภัย» ข้อผิดพลาดในการนำไปใช้อาจนำไปสู่สภาวะการแข่งขันที่ยากต่อการค้นหา ก่อนใช้ @unchecked ให้แน่ใจว่าประเภทนั้นไม่สามารถเขียนใหม่เป็นประเภทค่าได้จริงๆ
Actor และ Sendable เป็นเหรียญสองด้านเดียวกัน Actor แยกสถานะของมัน แต่เพื่อแลกเปลี่ยนข้อมูลกับโลกภายนอก มันต้องส่งคืนประเภท Sendable หากเมธอดของ actor ส่งคืนประเภทที่ไม่ใช่ Sendable คอมไพเลอร์จะแสดงคำเตือนหรือข้อผิดพลาด
เมื่อ actor ส่งข้อมูลไปยังโค้ดภายนอก ข้อมูลนี้ข้ามขอบเขตการแยก isolated Sendable รับประกันว่าผู้รับสามารถใช้ข้อมูลนี้อย่างปลอดภัยภายนอก actor ตัว actor เองยังคงถูกแยก isolated — สถานะภายในของมันไม่ถูกเปิดเผย
struct AccountSnapshot: Sendable {
let id: UUID
let balance: Double
let lastUpdated: Date
}
actor BankActor {
private var balance: Double = 0
func snapshot() async -> AccountSnapshot {
return AccountSnapshot(
id: UUID(),
balance: balance,
lastUpdated: Date()
)
}
}
AccountSnapshot คือ struct แบบ Sendable ที่มีเฉพาะคุณสมบัติ let ของประเภท Sendable วิธีการนี้เป็นแนวทางปฏิบัติที่ดีที่สุดสำหรับการดึงข้อมูลจาก actor ภาพรวมสถานะถูกส่งโดยค่า และ actor ไม่สูญเสียการควบคุมสถานะของมัน
@Sendable คือแอตทริบิวต์สำหรับฟังก์ชันและคลอเชอร์ที่รับประกันว่าคลอเชอร์จะไม่จับข้อมูลที่ไม่ใช่ Sendable ในรูปแบบที่เปลี่ยนแปลงได้ เมื่อคลอเชอร์ถูกส่งไปยัง Task หรือเมธอดของ actor มันจะต้องเป็น Sendable
คอมไพเลอร์ตรวจสอบว่าคลอเชอร์ @Sendable ไม่จับ การอ้างอิงที่เปลี่ยนแปลงได้ ไปยังคลาส การจับคุณสมบัติ let ของประเภท Sendable อนุญาตให้ทำได้ การจับตัวแปร var ของประเภทอ้างอิงจะทำให้เกิดข้อผิดพลาดเนื่องจากคลอเชอร์อาจถูกดำเนินการพร้อมกันกับการเปลี่ยนแปลง
func performAsync(operation: @Sendable () -> Void) {
Task {
await operation()
}
}
let constant = "Safe"
var counter = 0
// counter — การจับ var จะเปลี่ยน counter ทำให้เกิดข้อผิดพลาดในการคอมไพล์
performAsync { // ❌ การเปลี่ยนแปลงของตัวแปรที่ถูกจับ
print(constant)
}
กฎง่ายๆ: คลอเชอร์ @Sendable สามารถจับเฉพาะข้อมูลที่ตัวมันเองเป็น Sendable และจะไม่ถูกเปลี่ยนแปลงจากภายนอก สำหรับคลาส อนุญาตให้จับการอ้างอิงแบบอ่อน weak self หากคลาสไม่ได้ถูกทำเครื่องหมายเป็น Sendable ซึ่งป้องกันวงจรการอ้างอิงแบบคลาสสิกและสภาวะการแข่งขัน
คำถามที่พบบ่อย
Sendable คือมาร์กเกอร์ที่บอกคอมไพเลอร์: «ประเภทนี้สามารถส่งระหว่างเธรดได้อย่างปลอดภัย» โดยปกติ struct และค่าคงที่จะเป็น Sendable โดยค่าเริ่มต้น คลาสจะไม่เป็นเว้นแต่จะระบุอย่างชัดแจ้ง
ประเภทค่า (struct, enum) ที่มีคุณสมบัติ Sendable, คลาส final ที่มีสถานะไม่เปลี่ยนแปลง และประเภทพื้นฐาน Swift ทั้งหมด: Int, String, Double, Bool, Array, Dictionary, Optional — สอดคล้องกับ Sendable โดยอัตโนมัติ
@unchecked Sendable เป็นวิธีประกาศคลาสเป็น Sendable โดยไม่ต้องตรวจสอบเชิงสถิตจากคอมไพเลอร์ นักพัฒนารับประกันความปลอดภัยด้วยตัวเอง โดยมักผ่านล็อกหรือการดำเนินการอะตอมมิก
Actor แยกสถานะของมัน แต่เมื่อส่งคืนข้อมูลไปยังโค้ดภายนอก ข้อมูลนี้ข้ามขอบเขตการแยก isolated Sendable รับประกันว่าผู้รับจะไม่พบสภาวะการแข่งขันเมื่อใช้ข้อมูลนี้ภายนอก actor
เพิ่มโปรโตคอล Sendable ให้กับคลาส final ที่คุณสมบัติทั้งหมดเป็นค่าคงที่ของประเภท Sendable หากคลาสใช้ล็อก สามารถใช้ @unchecked Sendable ได้ แต่ต้องใช้ความระมัดระวัง
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ